【问题标题】:Return an Object in a thread safe class在线程安全类中返回一个对象
【发布时间】:2013-09-18 19:16:32
【问题描述】:

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

    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() 方法。

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

【问题讨论】:

  • 您在代码中使用的所有变量都是本地引用,这意味着它们不会在多个线程之间共享。所以,我看不出线程问题可能出在哪里,除非您隐藏的某些行是真正重要的行。
  • “局部引用”是您在方法体中声明的变量。这个变量不能被多个线程访问,除非你发布它们。发布变量意味着将其值分配给非本地引用(即实例/类字段)

标签: java concurrency thread-safety


【解决方案1】:

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

【讨论】:

  • 我猜你是对的,如果你用非不变的意思是不可变的。
  • 非不变是指可变和变异。
【解决方案2】:

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

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

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

这解决了我的线程安全问题。所以上面返回的对象在共享堆上的观点其实是对的。

【讨论】:

  • 尽管我对我在这个网站上的新手状态以及我从这里的其他帖子中拼凑出这个解决方案这一事实给予了应有的尊重,但你为什么要否决这个帖子?基本上归结为多线程程序的响应必须是线程安全的。乞丐不能成为选择者,但这几乎没有启发性。我从来不知道返回的对象是在共享堆上创建的。我发现这太棒了。老实说,这是一个相当大的架构错误。如果有比在课堂上同步更好的方法,我很想知道它是什么。我想也许与 ThreadLocal 相关...
  • 我对此投了反对票,而且我没有评论我这样做的原因绝对是错误的。由于已经过去了一段时间,我真的不记得为什么了。但我认为这是因为你确实有一个多线程问题,你正在通过添加一些同步来解决这个问题,即使根据你对问题的最初解释,它不应该是必需的。这可能意味着您通过更改线程交错来隐藏而不是消除一些 heisenbug。 IE。您正在损害性能(因为您正在序列化线程),并且该错误可能在将来有时仍会弹出。
猜你喜欢
  • 2018-04-11
  • 2011-04-07
  • 1970-01-01
  • 1970-01-01
  • 2015-03-16
  • 1970-01-01
  • 2022-07-27
  • 2013-01-26
  • 2013-12-10
相关资源
最近更新 更多