SwingUtilities.invokeLater

56
我的问题与 SwingUtilities.invokeLater 有关。我应该在什么情况下使用它?每次更新GUI组件时都必须使用吗?它实际上是做什么的?是否有替代方法,因为它似乎不直观并且添加了看似不必要的代码?

2
这是Control.BeginInvoke的等价物。 - SLaks
@SLaks这是否意味着如果我只有一个线程,我就不必使用它? - FadelMS
4
@FadelMS,由于Swing框架生成了其自己的线程(EDT),因此您将始终拥有该线程。 (没有处理用户点击等的“消息检查”循环,对吗?那么各种actionPerformed方法是如何被调用的呢?由EDT!) 因此,当您说您只有一个线程时,我必须理解为您只有EDT(例如,您创建了GUI并从主方法中退出)。在这种情况下,否,您 不需要 使用invokeLater。但是,您将无法进行任何“后台”处理。如果您这样做,将完全锁定GUI... - aioobe
可能是SwingUtilities.invokeLater做什么?的重复问题。 - Barett
5个回答

54

每次需要更新GUI组件时,我都必须使用吗?

不是的,在处理用户发起的事件(如单击和选择)时,如果您已经在事件派发线程(EDT)上,每次更新GUI组件时都不需要调用该方法。(例如 actionPerformed 方法总是由 EDT 调用。)

但是,如果您不在EDT上并且想要进行GUI更新(例如,如果需要从某个定时器线程或某个网络线程中更新GUI),则必须使用该方法调度更新以由EDT执行。

Swing基本上是线程不安全的。也就是说,所有与该API的交互都必须在单个线程(即EDT)上执行。如果您需要从另一个线程(例如定时器线程、网络线程等)进行GUI更新,则需要使用该方法(SwingUtilities.invokeLater、SwingUtilities.invokeAndWait等)。


@ Jens:这意味着如果我在同一个线程(Swing)上或者在运行时做一些事情,我就可以没有它。 - FadelMS
1
“或者在运行时执行操作”并不是很精确。如果您在运行时执行操作,比如在计时器线程中,那么您不能没有这些类型的方法。 - aioobe

15
Swing is single threaded and all changes to the GUI must be done on EDT 

invokeLater()的基本使用方法

  1. 主要方法应始终包装在invokeLater()中。

  2. 将延迟(但异步)的操作/事件放入EventQueue的末尾,

  3. 如果不存在EDT,则必须使用invokeLater()创建新的EDT。您可以使用if(SwingUtilities.isEventDispatchThread()) { ...进行测试。

  4. 虽然存在invokeAndWait(),但直到今天我(只是我的看法)仍然找不到使用invokeAndWait()而不是invokeLater()的理由,除了在GUI(JTree和JTable)中进行硬编码更改,但仅限于Substance L&F(对于测试EDT上事件一致性非常好)

  5. 基本内容:Swing并发性

  6. 来自后台任务的所有输出都必须包装在invokeLater()中。


7
每个Swing应用程序至少有两个线程:
  1. 主线程执行应用程序。
  2. EDT(事件分派线程)是一个更新UI的线程(以防止UI冻结)。
如果要更新UI,则应在EDT中执行代码。SwingUtilities.invokeLater,SwingUtilities.invokeAndWait,EventQueue.invokeLater,EventQueue.invokeAndWait等方法允许您通过EDT执行代码。

7
这次我的问题涉及到SwingUtilities.invokeLater,我应该在什么时候使用它呢?
关键是要理解Java有一个单独的线程(EDT)处理与Swing相关的事件。
您应该使用invokeLater()来显示桌面应用程序的主JFrame,而不是尝试在当前线程中执行此操作。它还会为以后优雅地关闭应用程序创建上下文。
对于大多数应用程序来说,就这样了。
我需要每次更新GUI组件时都使用它吗?它确切地做了些什么?
不需要。如果修改GUI组件,则会触发一个事件,Swing会注册该事件以供将来调度。如果有此事件的侦听器,则EDT线程将在某个时间点调用它。您不需要使用invokeLater(),只需正确设置组件上的监听器即可。
请记住,此线程是绘制屏幕上的帧等内容的同一线程。因此,侦听器不应执行复杂/长/占用CPU的任务,否则您的屏幕将冻结。
由于它听起来不直观并且似乎添加了不必要的代码,所以是否有替代方法呢?
您不需要编写比使用invokeLater() +在组件上感兴趣的侦听器显示应用程序更多的代码。其余部分由Swing处理。

3

大多数用户启动的事件(点击、键盘)已经在EDT上,因此您不需要使用SwingUtilities。这涵盖了很多情况,除了您的main()线程和更新EDT的工作线程。


一个用户可以更新被监视的文件,由一个更新GUI的线程来监控。 - Garrett Hall
1
嗯...那不算作是一个(直接的,规则适用的地方)用户触发事件:用户更新文件(在swing中,在EDT上)并保存它(通过单击按钮,在EDT上),这会触发一个操作系统文件更新事件(在swing外部,关闭EDT),通知到达监视器(关闭EDT),该监视器负责在EDT上更新GUI。 - kleopatra

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接