众所周知,更新 Swing GUI 必须专门在 EDT 中完成。更少的广告是阅读来自 GUI 的内容必须/应该在 EDT 中完成。例如,我们以 ButtonModel's isSelected() 为例方法,它告诉(例如)ToggleButton 的状态(“向下”或“向上”)。
在我见过的每个示例中,isSelected()
都是从主线程或任何线程自由查询的。但是当我查看 DefaultButtonModel 的实现时,它不是同步的,并且值不是易变的。因此,严格来说,如果 isSelected()
是从设置它的线程(即 EDT,当用户按下按钮时)以外的任何其他线程读取的,则它可能会返回垃圾。还是我弄错了?
当我对 Bloch 的 Effective Java 中的第 66 项感到震惊时,我最初想到了这个,这个例子:
public class StopThread {
private static boolean stopRequested;
public static void main(String[] args) throws InterruptedException {
Thread backgroundThread = new Thread(new Runnable() {
public void run() {
int i = 0;
while(!stopRequested) i++;
}
});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
stopRequested = true;
}
}
与看起来相反,该程序永远不会终止,至少在某些机器上是这样。从主线程更新 stopRequested
标志对后台线程是不可见的。这种情况可以通过同步的 getter 和 setter 或通过设置标志 volatile
来解决。
所以:
- 在 EDT(严格来说)之外查询 Swing 模型的状态是否错误?
- 如果不是,怎么会?
- 如果是,您如何处理?是靠运气,还是靠一些聪明的解决方法?调用并等待?
最佳答案
- 不,没有错,但是与任何跨线程通信一样,如果您决定这样做(例如使用
synchronized
或volatile
),您需要确保提供适当的线程安全机制。例如,我通常自己编写TableModel
实现,通常位于List<X>
其中X
是我的生意对象。如果我打算让其他线程查询列表,我会将其设为同步Collection
.同样值得注意的是,我通常永远不会更新List
来自其他线程;只查询它。 - 因为它与任何其他多线程应用程序的情况完全相同。
- 我通常使集契约(Contract)步(参见 1)。
警告
尽管我在上面做了回答,但我通常不会直接在 EDT 之外访问模型。正如 Carl 所提到的,如果执行更新的操作是通过某些 GUI 操作调用的,则无需执行任何同步,因为 EDT 已经在运行代码。但是,如果我希望执行一些会导致模型更改的后台处理,我通常会调用 SwingWorker
。然后分配 doInBackground()
的结果来自 done()
方法(即在 EDT 上)。这是更清洁的恕我直言,因为 doInBackground()
方法没有副作用。
关于java - 如果 Swing 模型的 getter 不是线程安全的,您将如何处理它们?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2092050/