SwingUtilities类中的invokeLater()和invokeAndWait()方法理解

发布时间:2023-05-06 09:39:00

在理解SwingUtilities类别中.invokeLater()和invokeAndWait()两种方法之前,首先要了解Swing线程机制。Swing程序通常包括三种类型的线程:①初始化线程(Initial Thread);②任务线程(Work Thread);③事件调度线程(Event Dispatch Thread,EDT)。

初始化线程主要负责启动程序的UI界面。一旦UI界面启动,初始化线程的工作就会结束。每个程序都从main方法开始,通常在初始化线程上运行。工作线程主要负责耗时任务和输入/输出密集型操作,与界面无直接关系。任何高染色或延迟UI事件的处理都将由该线程完成。EDT主要负责绘制和更新界面,并响应用户的输入。每个Swing程序都会有一个EDT,通过EDT管理一个事件队列。每次用户对界面的更新请求(如键盘、鼠标移动等)排列到事件队列中,等待EDT的处理。EDT将按顺序将队列中的事件分发给相应的事件监听器,并调用事件监听器中的回调函数。

通过以上内容,我们可以了解到EDT管理了一个事件队列,并按顺序分发。由于事件分发是一个单线程操作,只有在前一个事件监听器的回调函数完成后,才能执行组件更新的操作,并继续分发后一个事件。其后果之一是,当在事件监控回调函数中进行耗时操作时,UI界面将停止,UI界面上的所有控制器都将失效(不可触发)。针对这样的问题,我们想到的解决办法是将事件处理函数中耗时的操作放入新的线程(任务线程)中,而不是EDT中。例如,以下程序模拟提交数据的过程,单击提交按钮(putButton)检查数据和提交数据需要两个耗时的过程

SwingUtilities类中的invokeLater()和invokeAndWait()方法理解_Swing

点击提交按钮后,界面将停止。5秒后,UI界面显示“提交成功”的提示信息(完成两个耗时的操作和执行)。未显示“正在检查数据”和“正在提交数据”两个提示。这就是上面提到的,UI组件的刷新操作需要在事件监听函数执行后才能进行,然后在事件队列中发送下一个事件。以上代码修改如下(在新线程中耗时操作):

SwingUtilities类中的invokeLater()和invokeAndWait()方法理解_UI_02

UI界面依次显示“正在检查的数据”、“正在提交数据”、“成功提交”。这就是我们期待的结果。

但在编写Swing程序时,需要注意两点:①UI组件和事件处理器不能从其他非EDT线程访问,否则程序会出现非线程安全问题;②不能在EDT中执行耗时的任务,这将阻止更新UI界面的操作在队列中被处理,并使程序失去响应。在这种情况下,存在一个矛盾:耗时操作必须在工作线程中执行,否则UI界面将停止,而在工作线程中更新UI界面将出现非线程安全问题。为了解决这一矛盾,我们需要使用两种方法:invokelater()和invokeandwait()。通过这两种方法,我们可以使用可执行对象(Runnable)将实例添加到EDT的可执行事件队列中。例如,修改上述代码如下:

SwingUtilities类中的invokeLater()和invokeAndWait()方法理解_UI_03

在上述代码中,我们将更新UI界面的代码放入Runnable中,作为参数传输给invokelater。它将更新UI组件的事件放入EDT维护的事件队列中。EDT将在适当的时候从队列中取出Runnable并执行Run方法,这样,EDT仍然在更新UI界面而不会出现线程安全问题。

另外,Java 上述代码也可以通过Lambda表达式实现:

SwingUtilities类中的invokeLater()和invokeAndWait()方法理解_事件队列_04

invokeLater()和invokeAndWait()两种方法的区别

invokeLater()方法是异步的,即将在EDT中将事件放入事件队列后返回;invokeAndWait()方法是同步的,即在EDT中将事件放入事件队列,等待其Runnable完成后再返回,即invokeAndWait()方法会阻止调用该方法的线程,直到相应的Run方法完成。因此,EDT不能用来调用invokeAndWait()方法,否则很容易导致UI界面停止。

上一篇 LeetCode程序员面试金典:接雨水
下一篇 Java8新特性—Lambda表达式

文章素材均来源于网络,如有侵权,请联系管理员删除。

标签: Java教程Java基础Java编程技巧面试题Java面试题