为什么 Swing 组件应该只在事件调度线程上访问?

Why Swing components should be accessed on the Event Dispatch Thread only?

SwingWorker javadoc 中提到了上述语句。

在一个应用程序中,我看到一个冗长的后台任务在不同的线程中运行并且更新 UI 也没有问题(可以访问对 Swing 组件的引用)。

难道是有什么不好的事情要发生吗?

这是因为 Java memory model 不能保证一个线程的内存写入对其他线程可见,除非您使用某种形式的同步。为了性能和简单性,Swing 不是同步的。因此,EDT 可能永远看不到来自其他线程的写入。

您看到的应用程序可能大部分时间都可以工作,甚至可能在某些环境中一直可以工作。但是当它不起作用时,它会以难以重现的非常奇怪的方式失败。

所有 Swing 组件都实现为可从单个线程(事件调度线程)访问。所以没有针对变量和字段的并发访问和并发更改的保护。

如果幸运的话,一切都会顺利。但是您不能依赖它,同样的代码在下一个 运行.

上可能会出现大量问题

一个简单的例子:

JLabel 的绘制过程包含以下(简化的)代码(取自 BasicLabelUI class):

   # assume your label is enabled
   Icon icon = (label.isEnabled()) ? label.getIcon() : label.getDisabledIcon();

   # what happens if another thread calls label.setEnabled(false) at this point?

   if (icon != null) {
      icon.paintIcon(c, g, paintIconR.x, paintIconR.y);
   }

   if (label.isEnabled()) {
         paintEnabledText(label, g, clippedText, textX, textY);
   }
   else {
         paintDisabledText(label, g, clippedText, textX, textY);
   }

例如,如果您从 EDT 以外的其他线程调用 setEnabled(),您可能会得到一个带有启用图标但禁用绘制文本的标签。