返回线程安全类中的对象



我遇到了一个问题,似乎表明 Java 对象不是在我预期的堆上创建的。 简短的版本是我有一个JMS onMessage()方法,该方法调用另一个方法来根据TextMessage的JSon有效负载实例化正确的对象(路由命令)。

    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 消息被多次"处理()"。 当我设置 maxConcurrentConsumer=1 时,这不会发生。

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

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

实例化路由命令方法大约有 50 行长 - 这就是我一开始打破它的原因。 我可以检查 JMS 消息足以找出它代表的对象类型并在 onMessage() 方法中实例化它,并将实例化路由命令转换为仅在传递的对象上使用 setter。 这将满足上述规定。 但即使这样也不是完全微不足道的,并且会使 onMessage() 方法变得混乱。

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

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

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

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

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

这解决了我的线程安全问题。 因此,上面返回的对象位于共享堆上的观点实际上是正确的。

最新更新