在线程安全类中返回一个Object

时间:2013-09-18 19:16:32

标签: java concurrency thread-safety

我遇到的问题似乎表明Java对象没有在我期望的堆上创建。简短的版本是我有一个JMS onMessage()方法,它调用另一个方法来基于TextMessage的JSon有效负载实例化正确的对象(r​​outingCommand)。

    public void onMessage(Message message) throws RuntimeException {

IRoutingCommand routingCommand = null;
routingCommand = instantiateRoutingCommand(message);
...
process(routingCommand);
}

private IRoutingCommand instantiateRoutingCommand(Message message)  {

    IRoutingCommand routingCommand = null;

    ... lots of code to build the correct type of RoutingCommand

    routingCommand = new IeRoutingCommand(ieNotification);
    return routingCommand;
}

问题在于,在我的负载测试期间,在极度罕见的情况下,相同的JMS消息被多次“处理()编辑”。当我设置maxConcurrentConsumers = 1时,这不会发生。

我在互联网上发现了这个我从未知道的智慧宝石,但假设它是正确的,它可以解释我的问题:

对象的本地引用有点不同。引用本身不共享。但是,引用的对象不存储在每个线程的本地堆栈中。所有对象都存储在共享堆中。

instantiateRoutingCommand方法大约有50行 - 这就是为什么我一开始就把它打破了。我可以检查JMS消息,足以弄清楚它代表什么类型的对象,并在onMessage()方法中实例化它,并将instantiateRoutingCommand转换为仅在传递的对象上使用setter。这将满足上述规定。但即使这样也不是完全无足轻重的,会使onMessage()方法变得混乱。

我没有尝试使对象易变或进行任何同步,因为如果上述情况属实,那么我没有看到任何帮助。那么在多线程对象中处理传递对象的最佳方法是什么?

2 个答案:

答案 0 :(得分:1)

您必须同步访问多个线程引用的对象,或者为每个线程分配自己的副本。虽然后者可能是唯一有效的解决方案,但应注意复制的对象不包含也被复制的对象中包含的引用。

答案 1 :(得分:0)

我最终为我的RoutingCommand的所有子类创建了克隆方法。

然后在类上同步的块中使用它。由于实例化正确子类的方法只是一个解析器(没有I / O),因此阻塞所有线程并不是特别麻烦。

synchronized(FlowController.class) {
routingCommand = instantiateRoutingCommand(message).clone();  
}

这解决了我的线程安全问题。所以返回对象的上述点在共享堆上实际上是正确的。