为什么Swing线程模型被认为是错误的,应该如何?

我多次听说Java Swing线程模型是错误的。 我不完全理解为什么,我知道这个问题与你可以从主UI线程以外的另一个线程Drawable这一事实有关。 我知道有一些实用function,如SwingUtilities.invokeAndWaitSwingUtilities.invokeLater ,可以让你在Runnable绘制,而Runnable则由Event Dispatcher线程运行。 我猜这种方式可以确保绘画是同步完成的,这不会使缓冲区处于不稳定的状态。

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

Brian Goetz的Java Concurrency in Practice ,

9.1为什么GUI是单线程的?

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

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

SWT实现了单线程用户界面模型,通常称为单元线程。 在此模型中,只有用户界面线程可以调用用户界面操作。 严格执行此规则。 如果您尝试从用户界面线程外部访问SWT对象,您将获得SWTException(“无效的线程访问”)。

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

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

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

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

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

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

如果您不使用EDT并提交一些长时间运行的更改,例如按下按钮后计算某些值,您可以看到应用程序无响应,而不是重新绘制自身。 因为EDT忙着为你做计算,没有时间安排重新绘制和其他事件。

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

如果从用户启动的事件执行长时间运行的任务,而不使用分离的线程来运行该任务,则可能会出现问题 – 问题是,在任务运行时,应用程序将冻结。 不会发生重新绘制,用户将无法与它进行全部交互,它看起来就像应用程序刚刚锁定自己一样。

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

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