15

SAAJ の SOAPConnectionFactory および MessageFactory クラスを複数のスレッドで使用したいのですが、それらがスレッドセーフであると想定できないことがわかりました。関連する投稿:

これがスレッドセーフであるという興味深い小さな証拠があります: http://svn.apache.org/repos/asf/axis/axis2/java/core/tags/v1.5.6/modules/saaj/src/org/apache /axis2/saaj/SOAPConnectionImpl.java という

SAAJ 仕様ではスレッド セーフは明示的に要求されていませんが、Sun の参照実装の SOAPConnection はスレッド セーフであるようです。

それでも、SAAJ クラスをスレッドセーフとして扱うには十分な証拠ではないと思います。

だから私の質問:以下のイディオムは正しいですか?メイン スレッド内でスレッド セーフでない可能性があるファクトリを使用して SOAPConnection オブジェクトと MessageFactory オブジェクトを 1 つだけ作成し、CompletionService インターフェイスの事前発生保証を使用して、これらのオブジェクトをエグゼキュータ タスクに安全に発行します。また、この事前発生保証を使用して、結果の HashMap オブジェクトを抽出します。

基本的には、自分の推論の正気を確認したいだけです。

public static void main(String args[]) throws Exception {
    ExecutorService executorService = Executors.newFixedThreadPool(10);
    CompletionService<Map<String, String>> completionService = new ExecutorCompletionService<>(executorService);

    //submitting 100 tasks
    for (int i = 0; i < 100; i++) {
        // there is no docs on if these classes are thread-safe or not, so creating them before submitting to the
        // external thread. This seems to be safe, because we are relying on the happens-before guarantees of the
        // CompletionService.
        SOAPConnectionFactory soapConnectionFactory = SOAPConnectionFactory.newInstance();
        SOAPConnection soapConnection = soapConnectionFactory.createConnection();
        MessageFactory messageFactory = MessageFactory.newInstance();
        int number = i;// we can't just use i, because it's not effectively final within the task below
        completionService.submit(() -> {
            // using messageFactory here!
            SOAPMessage request = createSOAPRequest(messageFactory, number);
            // using soapConnection here!
            SOAPMessage soapResponse = soapConnection.call(request, "example.com");
            soapConnection.close();
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            soapResponse.writeTo(outputStream);
            // HashMap is not thread-safe on its own, but we'll use the happens-before guarantee. See f.get() below.
            Map<String, String> result = new HashMap<>();
            result.put("soapResponse", new String(outputStream.toByteArray()));
            return result;

        });
    }

    // printing the responses as they arrive
    for (int i = 0; i < 100; i++) {
        Future<Map<String, String>> f = completionService.take();
        Map<String, String> result = f.get();
        System.out.println(result.get("soapResponse"));
    }

    executorService.shutdown();
}

/**
 * Thread-safe static method
 */
private static SOAPMessage createSOAPRequest(MessageFactory messageFactory, int number) throws Exception {
    SOAPMessage soapMessage = messageFactory.createMessage();
    SOAPPart soapPart = soapMessage.getSOAPPart();

    String serverURI = "example.com";

    SOAPEnvelope envelope = soapPart.getEnvelope();
    envelope.addNamespaceDeclaration("example", serverURI);

    SOAPBody soapBody = envelope.getBody();
    SOAPElement soapBodyElem = soapBody.addChildElement("number", "example");
    soapBodyElem.addTextNode(String.valueOf(number));

    soapMessage.saveChanges();

    return soapMessage;
}
4

3 に答える 3

2

私はあなたのコードをテストしました。完全に問題のないsoapConnectionFactoryを介してsoapConnectionを作成しているようです。SAAJ 1.3 の次のメソッドは、MessageFactory の新しいインスタンスを返します。

public static MessageFactory newInstance(String protocol) throws SOAPException {
    return SAAJMetaFactory.getInstance().newMessageFactory(protocol);
}

スレッドセーフについての説明には情報がありませんが、コードを見ると、このメソッドは主にスタック変数を使用しているように見えます。たとえば、スタックに SOAPConnection オブジェクトがあり、それを使用しているようです。同期ブロックがないにもかかわらず、soapConnection.call(request, "example.com") が複数のスレッドによって呼び出された場合、問題は見られません。

スレッドが異なる接続を介して結果メッセージを送信することが予想されます

于 2016-03-25T17:02:17.007 に答える