7

我多次听说 Java Swing 线程模型是错误的。我不完全理解为什么,我知道问题与您可以Drawable从主 UI 线程以外的另一个线程绘制 a 的事实有关。我知道有一些实用功能,例如SwingUtilities.invokeAndWaitSwingUtilities.invokeLater可以让您在 a 中进行绘画Runnable,而后者又由 Event Dispatcher 线程运行。我想这样可以确保绘制是同步完成的,并且不会使缓冲区处于不一致的状态。

我的问题是:“好”的 UI 工具包表现如何?采用了哪些解决方案?

4

4 回答 4

9

Brian Goetz 的Java 并发实践

9.1 为什么 GUI 是单线程的?

...在过去,GUI 应用程序是单线程的,GUI 事件是从“主事件循环”处理的。现代 GUI 框架使用的模型略有不同:它们创建了一个专用的事件调度线程 (EDT) 来处理 GUI 事件。单线程 GUI 框架并不是 Java 独有的;Qt、NextStep、MacOS Cocoa、X Windows 和许多其他软件也是单线程的。这不是因为缺乏尝试。编写多线程 GUI 框架的尝试已经很多,但是由于竞争条件和死锁的持续问题,它们最终都到达了单线程事件队列模型,其中一个专用线程从队列中获取事件并将它们分派给应用程序-定义的事件处理程序...

于 2012-11-22T21:13:40.113 回答
3

对于 SWT:http ://book.javanb.com/swt-the-standard-widget-toolkit/ch05lev1sec7.html

SWT 实现了通常称为单元线程的单线程用户界面模型。在这个模型中,只有用户界面线程可以调用用户界面操作。这条规则是严格执行的。如果您尝试从用户界面线程外部访问 SWT 对象,您将收到 SWTException("Invalid thread access")。

所以 SWT 也是单线程的。但它需要额外的步骤来禁止在 UI 线程之外对 UI 进行任何更改。考虑 Swing 中的替代方案,允许从其他地方修改 UI,但迟早会产生意想不到的结果,这将使新手程序员感到困惑,然后他们将了解到 Swing 是单线程的“硬”方式。

此外,如果您的设计不清楚,您最终可能会遇到您认为自己处于正确线程但实际上并非如此的情况。您也可能无法可靠地判断哪些线程将访问特定的代码,但无论如何您自己的代码中可能存在严重的设计问题。

除此之外,我无法想象 Swing 的线程模型会被视为“错误”的其他原因。

于 2012-11-22T21:27:32.693 回答
1

当前显示技术的实现方式,在屏幕上绘制像素始终是串行的。您需要每秒生成大约 30 张图像,并一张一张地绘制它们。

所以这幅画不需要多线程,因为你仍然需要在后台做一些同步。这实际上就是 Swing 正在做的事情——它使用一个名为 Event Dispatch Thread 的特殊线程来安排所有更改在下一张图像之前及时发生。

所以从技术上讲,Swing 是线程安全的,如果您使用 EDT 提交更改。这就是目的invokeLater()invokeAndWait()方法。他们向 EDT 提交更改。

如果您不使用 EDT 并提交一些长时间运行的更改,例如在按下按钮后计算一些值,您会看到应用程序变得无响应,而不是重新绘制本身。因为 EDT 正忙于为您计算,没有时间安排重绘和其他活动。

于 2012-11-22T20:57:45.100 回答
0

Swing 有一个线程,主要负责让用户与应用程序的图形部分进行交互。如果您只执行快速任务以响应用户发起的事件,您的应用程序将始终响应。

如果您从用户启动的事件执行长时间运行的任务,而不使用单独的线程来运行该任务,您可能会遇到问题 - 问题是,当任务运行时,应用程序将冻结。不会发生重绘,用户将无法与之交互,看起来应用程序只是将自己锁定了。

如果您在单独的线程中运行任务(例如,您正在下载一个页面并且您想通知用户下载已完成),那么您不能直接从该任务更新 Swing,而必须使用一个您在问题中提到的辅助方法。

创建这些任务是一个更加劳动密集型的过程,以便您的应用程序始终响应 - 但是如果您正在运行需要很长时间的东西(下载文件就是一个很好的例子),即使在任务被执行,你甚至可以让用户取消任务,只要任务本身允许。您可以使用模式对话框来防止用户在执行任务时执行任何其他操作(如果您想这样做),或者使用显示旋转轮或类似内容的进度对话框。但我想重要的是不要让用户认为应用程序只是无缘无故地“冻结”了。

于 2012-11-22T20:55:32.763 回答