SwingUtilities.invokeLater()和invokeAndWait()的使用

SwingUtilities.invokeLater()和SwingUtilities.invokeAndWait()的使用

事件分发线程:

       Swing中事件处理和绘画代码都在一个单独的线程中执行,这个线程就叫做事件分发线程。这就确保了事件处理器都能串行的执行,并且绘画过程不会被事件打断。为了避免死锁的可能,你必须极度小心从事件分发线程中创建、修改、查询Swing组件以及模型。

       在Java中Swing是线程不安全的,是单线程的设计,这样的造成结果就是:只能从事件派发线程访问将要在屏幕上绘制的Swing组件。事件派发线程是调用paint和update等回调方法的线程,它还是事件监听器接口中定义的事件处理方法,例如,ActionListener中的actionPerformed方法在事件派发线程中调用。

       Swing是事件驱动的,所以在回调函数中更新可见的GUI是很自然的事情,比如,有一个按钮被按下,项目列表需要更新时,则通常在与该按钮相关联的事件监听器的actionPerformed方法中来实现该列表的更新,从事件派发线程以外的线程中更新Swing组件是不正常的。

       有时需要从事件派发线程以外的线程中更新Swing组件,例如,在actionPerformed中有很费时的操作,需要很长时间才能返回,按钮激活后需要很长时间才能看到更新的列表,按钮会长时间保持按下的状态只到actionPerformed返回,一般说来耗时的操作不应该在事件处理方法中执行,因为事件处理返回之前,其他事件是不能触发的,界面类似于卡住的状况,所以在独立的线程上执行比较耗时的操作可能更好,这会立即更新用户界面和释放事件派发线程去派发其他的事件。

       SwingUtilities类提供了两个方法:invokeLate和invoteAndWait,它们都使事件派发线程上的可运行对象排队。当可运行对象排在事件派发队列的队首时,就调用其run方法。其效果是允许事件派发线程调用另一个线程中的任意一个代码块。

       只有从事件派发线程才能更新组件。


invokeLater:后面的程序和这个参数的线程对象可以并行,异步地执行
invokeLater一般用于在线程里修改swing组件的外观,因为swing组件是非同步的,所以不能在线程中直接修改,会不同步,得不到期望的效果,所以要把修改外观的代码放在一个单独的线程中,交给invokeLater:后面的程序和这个参数的线程对象可以并行,异步地执行

这样做的目的是为了使部件的外观与其事件处理能够协调。比如,你点击一个按钮,并且在该按钮的onClick事件里有处理代码。那么当按钮弹起来的时候,你就会知道,处理代码执行过了。当然也会相互影响,比如,你的代码运行时间很长,你就会看到该按钮很长时间不能弹起来,并且整个这段时间整个窗体其它部件也不再对任何输入作出响应。
有些情况下,二者的联系不是很密切,你希望部件能够立即显示或重画,而处理代码在后台运行,那么你就可以使用invokeLater。invokeLater实际上就是把处理代码放到Event-Dispatch线程之外的线程中运行,而部件可以立即执行重画操作。

invokeAndWait:后面的程序必须等这个线程(参数中的线程)的东西执行完才能执行
invokeAndWait指的是当前线程阻塞,一直等待invokeAndWait中指定的代码执行完毕之后,才接着运行,这种方式用的不多。


使用invokeLater方法

你可以从任何线程中调用invokeLater来请求事件分发线程以运行某段代码。你必须将这段代码放入一个Runnable对象的run方法中,并将该指定Runnable对象作为参数传递给invokeLater。invokeLater函数会立即返回,不会等到事件分发线程执行完这段代码。

即:invokeLater必须放在run()方法体内。


使用invokeAndWait方法

invokeAndWait方法和 invokeLater方法一样,除了invokeAndWait是直到事件分发线程已经执行了指定代码才返回。任何可能的时候,你都应当使用 invokeLater而不是invokeAndWait——因为invokeAndWait很容易引起死锁。如果你使用invokeAndWait,要 保证调用invokeAndWait的线程不持有任何其他线程在调用时刻也会需要的锁。


使用线程提高性能

使用恰当的话,线程会是 一个很有用的工具。然而,当在一个Swing程序中使用线程时,你必须谨慎处理。虽然有危险,但线程还是很有用的。你可以使用线程提高你程序的响应性能。而且,线程有时还能简化程序的代码或结构。这里有一些使用线程的特殊场景:

·        将一个比较消耗时间的初始化任务移出主线程,可以使GUI出现得更快。耗时任务包括做额外的计算任务以及网络阻塞或硬盘I/O(比如,载入图片)。

·        将耗时任务移出事件分发线程,从而使GUI同时继续响应用户操作。

·        为了能重复执行一个操作,通常还需要在操作之间设置一个预置时间段(定时器)。

·        等待其他程序的消息。

如果你需要创建一个线程,可以通过使用工具类SwingWorker或是Timer类中的一个实现该线程,这样一来能够避免一些常见的陷阱。SwingWorker对象创建一个线程以执行一个耗时操作。

当该操作结束后,SwingWoker会在事件分发线程中给你提供执行额外代码的选项。Timer类则适合重复执行或需要一段延时执行的操作。如果你需要实现自己的线程,可以在Concurrency中找到相关信息。


可以使用一些技巧来使多线程的Swing程序有更好的性能:

·        如果当你需要更新一个组件但事件监听器中的代码并没有执行的时候,可以考虑使用这两个方法,SwingUtilities的invokeLater(优先选项)或invokeAndWait方法。

·        如果你不能确定事件监听器中的代码 是否已经执行,那你就应当分析程序代码和线程中每个函数的调用文件。如果还是不行,可以使用SwingUtilities的 isEventDispatchThread方法。当该方法在事件分发线程中执行时返回true。你能在任何线程中安全地调用invokeLater,但 invokeAndWait会在调用线程不是事件分发线程时抛出异常。

·        如果你需要在一段延迟之后更新组件(不论你的代码目前是否正在一个事件监听器中运行),那就使用定时器timer吧。

·        如果你需要在没经过一段规律的时间间隔后更新组件,使用定时器timer

 

 转载参见:

http://www.cnblogs.com/langtianya/archive/2012/12/18/2822638.html

http://mxdxm.iteye.com/blog/672107

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值