如果Swing模型的getter不是线程安全的,你如何处理它们?

Joo*_*kka 15 java swing thread-safety

众所周知,更新Swing GUI必须仅在EDT中完成.较少的广告是从GUI 中读取内容必须/也应该在EDT中完成.例如,让我们使用ButtonModel的isSelected()方法,它告诉(例如)ToggleButton的状态("向下"或"向上").

在我看过的每个例子中,isSelected()都是从主要或任何一个线程中自由地查询.但是当我查看DefaultButtonModel的实现时,它没有同步,并且值不是volatile.因此,严格地说,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;
    }
}
Run Code Online (Sandbox Code Playgroud)

与看起来相反,该程序永远不会终止,至少在某些机器上.stopRequested从主线程更新标志对后台线程是不可见的.可以使用同步的getter和setter或通过设置标志来修复这种情况volatile.

所以:

  1. 在EDT之外查询Swing模型的状态(严格来说)是错误的吗?
  2. 如果没有,怎么样?
  3. 如果是的话,你如何处理它?运气好,还是通过一些聪明的解决方法?InvokeAndWait?

Ada*_*ski 4

  1. 不,没有错,但是与任何跨线程通信一样,如果您决定这样做(例如使用synchronizedor volatile),您需要确保提供适当的线程安全机制。例如,我通常编写自己的TableModel实现,通常位于我的业务对象所在List<X>的位置。X如果我打算让其他线程查询列表,我会将其设为同步Collection。还值得注意的是,我通常不会List从其他线程更新;只能查询一下。
  2. 因为它与任何其他多线程应用程序的情况完全相同。
  3. 我通常使集合同步(参见 1)。

警告

尽管我在上面回答了,但我通常不会直接访问 EDT 之外的模型。正如 Carl 提到的,如果通过某些 GUI 操作调用执行更新的操作,则无需执行任何同步,因为代码已由 EDT 运行。但是,如果我希望执行一些后台处理,这将导致模型发生更改,我通常会调用 a ,然后从方法内(即在 EDT 上)SwingWorker分配结果。恕我直言,这是更干净的方法,因为该方法没有副作用。doInBackground()done()doInBackground()

  • 我不得不不同意#1,因为它会误导新手。他们应该学习“在 EDT 之外进入摇摆是禁忌”的规则,并且只有在他们完全理解为什么他们应该打破规则之后。新手只会看到你的前三个字“不,没有错......”。更不用说,线程化已经足够困难了,因为它不必担心您是否正确同步模型。我没有投票反对你的唯一原因是因为你的警告部分是处理它的正确方法。 (2认同)