我有一个字符串,它将以不可预测的顺序由四个不同的线程访问。
String s = "When value is";
现在上面的字符串s将通过Java Swing EDT由四个不同的线程更新。我有两个JCombobox和两个JTextField。在这些组件的内部事件处理程序中,我将不得不更新上面的字符串。
例如 当textfield焦点更改时,字符串应为“当值为X时” 当组合更改时,字符串应为“当值小于X和Y”时
所以就像上面四个线程将改变一个字符串。如果我不控制它们输出是意料之外的。解决这个问题的最佳方法是什么?我可以使用join()或者可以使用volatile,但它会使代码变得复杂。
请分享您的想法。提前谢谢。
答案 0 :(得分:6)
首先,String
是不可变的,因此无法更新。然而,另一个对象(例如Swing组件)可以保存对String
的引用,并且可以更新此引用以指向不同的String
。这是在GUI中更新文本时通常会发生的情况。
如果您有四个不同的线程需要更新Swing组件,例如要显示不同的String
,他们应该通过使用SwingUtilities
方法invokeLater
或invokeAndWait
排序要在单个事件调度线程上运行的任务来执行此操作,例如
SwingUtilities.invokeLater(new Runnable() {
public void run() {
myComponent.setText("New string");
}
};
或者在更复杂的应用程序中,您可能希望从这四个线程中的每一个更新域对象,然后需要线程安全,然后分别更新EDT上的Swing组件。在我看来,创建线程安全的代码并不容易。我强烈推荐本书Java Concurrency in Practice,但重点是确保一次只有一个线程执行涉及有问题的对象暂时处于不一致状态的任何操作。有时可以通过适当使用现有的线程安全类和原子操作来实现,但可能需要使用锁,通常通过synchronized
方法或块。
当然,如果现在看来你没有四个线程,只有四个事件处理程序在EDT上调用,那么这一点都不相关。
答案 1 :(得分:1)
正如评论中提到的,只有一个事件派发线程。除非你不正确地使用Swing(并且 Ben 的答案说明了这一点),你真正的问题是你不知道事件被触发的顺序。
即使对于单个组件,也无法控制此顺序。尝试使用 join 或某种形式的锁定机制只会挂起您的应用程序。
解决方案是创建一个介于输入源之间的对象。根据您的描述,这可能是一个简单的Map<String,String>
,其中键是事件源,值是您最终想要显示的内容。
您的动作侦听器每个都会向此地图添加一个值,当添加第4个值时,您可以决定将哪个值写入输出。
然后清除地图,等待下一组输入。
答案 2 :(得分:1)
您应该编写程序,并假设处理事件的顺序是正确的顺序。
让我们想一下,你想要多个事件到同一个领域,顺序非常重要。但是,您不相信EDT会按您需要的顺序通知事件。您能否举例说明订单的错误位置以及您打算如何纠正订单?