首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >每个线程创建一个非线程安全对象并在保证之前使用

每个线程创建一个非线程安全对象并在保证之前使用
EN

Stack Overflow用户
提问于 2016-03-22 10:29:42
回答 3查看 2.1K关注 0票数 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对象,然后在保证CompletionService接口之前安全地将这些对象发布到执行器任务中。我还使用这种情况--在保证提取结果HashMap对象之前。

基本上,我只是想证实我的推理是正确的。

代码语言:javascript
复制
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;
}
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2016-03-30 01:56:47

是的,您对CompletionService的推理是正确的-- .submit()确保任务lambda将看到完整的对象,.take()确保主线程只看到完全形成的响应。

但是,一般来说,您不需要这样做。静态工厂方法应该始终是线程安全的,因为在不了解整个JVM的情况下,无法确保它们在其他线程中不被使用,而且在许多环境中都无法真正编写依赖于此的代码。有时候,如果一个线程试图使用它,而另一个线程正在配置它,您会看到一个实现可能会出现问题,但即使是这样的情况也很少见。

想象一下使用SOAPConnectionFactory的servlet。不可能知道在同一个JVM中没有其他Web应用程序没有同时使用它,所以它必须是线程安全的。

所以,真的,如果MessageFactory.newInstance()和SOAPConnectionFactory.newInstance()不是线程安全的话,它们就错了。我会在多个线程中使用它们,而不用担心,如果您真的担心的话,只需检查源代码。但他们真的很好。

另一方面,静态工厂方法创建的对象(甚至其他工厂)通常不是线程安全的,您不应该假设它们没有这样的文档。即使检查源代码也是不够的,因为如果没有记录接口是线程安全的,那么以后会有人将不安全状态添加到实现中。

票数 7
EN

Stack Overflow用户

发布于 2016-03-25 22:52:49

我花了一个小时查找com.sun.xml.internal.messaging.saaj的源代码(在Oracle中用作默认的SAAJ实现),发现WhateverFactory.newInstance()返回的工厂都没有任何内部状态。因此,它们绝对是线程安全的,不需要多次实例化。

这些工厂是:

  • SOAPConnectionFactory -- client.p2p.HttpSOAPConnectionFactory
  • MessageFactory -- 1脉冲

例如,HttpSOAPConnectionFactory实际上只有3行代码:

代码语言:javascript
复制
public class HttpSOAPConnectionFactory extends SOAPConnectionFactory {

    public SOAPConnection createConnection() throws SOAPException {
        return new HttpSOAPConnection();
    }
}

那么SOAPMessageSOAPConnection呢--它们必须在一个线程中使用,尽管对它们进行的操作涉及几个调用。(事实上,SOAPConnection#call()也是线程安全的,因为http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/com/sun/xml/internal/messaging/saaj/client/p2p/HttpSOAPConnection.java除了closed变量之外不包含任何内部状态。可以,但是不应该被重用,除非您保证永远不会调用.close(),否则后续的.call()会抛出。)在完成处理之后,应该关闭和忘记SOAPConnection,以及在特定的请求-响应周期中使用的SOAPMessage实例。

最后,我相信你做的一切都是正确的,除了为每个电话建立独立的工厂。至少在上述实现中,这些工厂完全是线程安全的,因此可以在加载类时保存。

所有这些都是指Oracle JDK附带的默认SAAJ实现。如果您使用商业Java应用服务器(Websphere、JBoss等),其中实现可能是特定于供应商的,那么最好将您的问题提供给他们的支持。

票数 4
EN

Stack Overflow用户

发布于 2016-03-25 17:02:17

我测试了您的代码,似乎您正在通过soapConnectionFactory创建一个soapConnectionFactory,这是非常好的。SAAJ1.3中的以下方法返回MessageFactory的一个新实例

代码语言:javascript
复制
public static MessageFactory newInstance(String protocol) throws SOAPException {
    return SAAJMetaFactory.getInstance().newMessageFactory(protocol);
}

在描述线程安全性方面没有任何信息,但是通过查看代码,该方法似乎主要使用堆栈变量,例如在堆栈中有一个SOAPConnection对象并使用它。如果多个线程调用soapConnection.call(request,"example.com"),尽管没有同步块,我看不到问题。

人们会期望线程通过不同的连接发送它们的结果消息。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/36151933

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档