【问题标题】:Making Swing components synchronized使 Swing 组件同步
【发布时间】:2011-06-30 23:51:13
【问题描述】:

我正在阅读 Java 线程 第 3 版。奥克斯和黄(O'Reilly 2004)。 他们在整本书中都有一个 Swing 打字游戏的例子。 他们定义的类大多是javax.swing.JComponent的自定义子类。

对我来说似乎很不对劲的是,他们使用各种同步方法使JComponents 线程安全。我的印象是 Swing 组件不应该是线程安全的,而是应该始终从 Swing 事件调度线程访问它们。 (有趣的是,他们通过 Swing EDT 修改组件的少数几次之一是针对 setText,这是极少数不需要从 EDT 调用的 Swing 方法之一。)

我想从你们当中有很多编写/阅读 Swing 代码经验的人那里知道: 程序员让 Swing 组件同步而不是总是通过 EDT 修改它们是否常见?可以忍受吗?

编辑:
我注意到这与thread 几乎是同一个问题。然而,它并没有说明程序员在野外实际做了什么。 O'Reilly 的书会如此公然违反 Swing 线程模型,这让我感到困惑。

编辑:
我发现他们确实在书的中间某处简要解释了 Swing 线程模型。尽管如此,我想回答我的问题。我感觉大多数读过这本书的人最终都会违反 Swing 线程模型,因为他们的大多数示例都是这样做的。

编辑:
如果你想看代码,你可以Download examples code作为一个zip文件。参见例如 ch03/example1/AnimatedCharacterDisplayCanvas。

编辑:
我刚刚了解到setText 在 Java7(2011 年 7 月发布)中不是线程安全的。

【问题讨论】:

  • 我总是确保在 EDT 上完成更新。
  • 你错了:setText 必须从 EDT 调用!
  • @jfpoilpret 根据API,它是线程安全的。所有JTextDocuments 都包含一个内部AbstractDocument,它有一个写锁。无论您是否从 EDT 调用 setText 都没有区别,因为必须获得锁。 (但是getText 不是线程安全的。)
  • 想一想:在 EDT 上调用 setText 可能是个坏主意,因为它可能会在等待文档锁定时卡住。
  • setText 在 Java 7 中并没有变得不安全,它从来就不是安全的,与文档相反。 Java 7 修复了文档以反映现实。

标签: java multithreading swing


【解决方案1】:

简单地说,只要同步方法不在EventQueue 上执行,它们就不会阻塞事件调度线程。相反,在另一个线程上执行的方法应该始终使用EventQueue 通过invokeLater()invokeAndWait() 或相关机制(例如javax.swing.Timerjavax.swing.SwingWorker)调度代码。实际上,这些都是可靠的。示例可能是正确的,但应该从这个角度进行检查。

EventQueue API 说:“唯一的要求是 事件......被调度......以与它们入队相同的顺序。”在我看来,这相当于java.util.concurrent的“发生之前”关系 和 JLS。更详细的讨论可以在here找到。

【讨论】:

  • 在书中,他们同步paintComponent,因为它访问了一个被其他线程修改的对象。它们还同步修改此对象的方法。我认为他们所做的工作有效;只是它不尊重 Swing 线程模型。如果示例变得更复杂,那么在不让 GUI 变得迟钝的情况下保持同步可能会很麻烦(这就是没有多线程 GUI 的原因)。
  • 他们不使用invokeLaterinvokeAndWait。例如,某个线程生成一个随机字符,该字符通过该组件的同步设置方法设置为JComponent 的成员。该组件的paintComponent 也被同步,因为它打印出 char 成员。
  • 我想我的问题最简单的形式是:有没有人见过同步的paintComponent
  • 我在 4 小时前给了你答案。将更新放在 EDT 上,忘记同步!这就是 EDT 和单线程规则的重点。如果你遵循它,你不会有任何问题。在我多年来在论坛上发帖的过程中,我从未见过有人建议同步绘画方法或任何其他 Swing 方法(我记得)。
  • 我听从@camickr 的更大经验,同步绘画对我来说也是陌生的。我可以看到 liveness 会受到怎样的影响。作为参考,有一个相关成语的总结here。顺便说一句,我在 Ada 中使用了protected types 以按照您描述的方式同步对 Carbon API 的访问,但如果我不得不重新做一遍,我会构建一个等效的 EDT。
【解决方案2】:

你不应该在 Swing 组件上使用同步块,当它试图被渲染时会导致奇怪的问题。

Swing 不是线程安全的,因为一切都应该在 EDT 上更新,甚至是 Swing 组件的创建。

应将长时间运行的进程移至后台线程或 SwingWorker。当除 EDT 之外的线程需要制作组件或更新组件时,应使用 SwingUtilities.invokeLater() 对其进行包装

【讨论】:

  • 关于您的第一段:是的,如果 Swing EDT 正在等待锁定,则整个 GUI 将被锁定。不过,对于书中的简单示例,我认为这不是问题(我没有尝试运行代码)。
  • 仍然没有理由让摆动组件保持锁定。因为它的单线程(EDT)竞争条件是不可能的。在 Swing 应用程序中对类进行锁定是可以的,但不应从 EDT 调用它们
【解决方案3】:

"Swing 组件本质上不是线程安全的,并且作为一般规则,在 Swing 组件在屏幕上可见之后,您只能从事件线程安全地修改它们的数据。如果您从任何线程修改 Swing 组件数据“ 莫妮卡·帕兰 http://java.sun.com/developer/technicalArticles/Threads/swing/

【讨论】:

  • -1 引用的文章已过时(2001 年)。 Sun 最近(2009 年或 2010 年)使线程安全声明更加笼统:甚至组件创建也必须在 EDT 中完成。
  • 是的,但是这本书是在 2004 年之前出版的,答案是针对那个具体案例
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-06-16
  • 1970-01-01
  • 1970-01-01
  • 2020-01-19
  • 2016-03-12
  • 2016-09-03
  • 1970-01-01
相关资源
最近更新 更多