转自http://www.codeceo.com/article/swing-swingutilities-invokelater.html

译者注:本文其实是一个各方见解评论的总结,已将其中于此处发布不妥的内容去掉,全文内容可查看原文。

在官方的文档里:http://docs.oracle.com/javase/tutorial/uiswing/painting/step1.html 告诉我们如何创建一个gui。

事件分发线程:

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

注意:我们过去常说只要你没有修改已经实现过的组件,你就能在主进程中创建GUI。[补充:下面页注中的红色字体。] 已实现过的意思是组件已经在屏幕上描绘出来或是准备描绘了。方法setVisible(true)和pack可以实现一个窗口,反过来又可以实现该窗口内 包含的组件。尽管这对大多数应用程序都管用,但这种做法在某些情况下会引起一些问题。在Swing Tutorial的所有示例中,我们只在ComponentEventDemo中遇到一个问题。在那个样例中,有时候当你载入样例后,它并不会启动。因为 如果在文本域还没实现的时候就去更新会出现死锁,但是其他的时候没有意外的话它也是会正常启动。

为了避免线程问题,建议你使用invokeLater在事件分发线程中为所有新应用程序创建GUI。如果你的现有程序能工作正常,那你可能就会让它保持下去;然而,如果改造起来方便的话,还是希望你能改造一下。

你可能已经注意 到,大部分教程中的例子都使用一个标准的主函数,即SwingUtilities的函数invokeLater来保证GUI在事件分发线程中创建。这里有 一个从FocusConceptsDemo例子中提取的主函数的样例。我们还将处理创建GUI事件的主函数都要调用的一个私有静态方法,即 createAndShowGUI 的源代码包含进来了。

/*** Create the GUI and show it.  For thread safety,* this method should be invoked from the* event-dispatching thread.*/
private static void createAndShowGUI() {//Make sure we have nice window decorations.JFrame.setDefaultLookAndFeelDecorated(true);//Create and set up the window.frame = new JFrame("FocusConceptsDemo");frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//Create and set up the content pane.JComponent newContentPane = new FocusConceptsDemo();newContentPane.setOpaque(true); //content panes must be opaqueframe.setContentPane(newContentPane);//Display the window.frame.pack();frame.setVisible(true);
}public static void main(String[] args) {//Schedule a job for the event-dispatching thread://creating and showing this application's GUI.javax.swing.SwingUtilities.invokeLater(new Runnable() {public void run() {createAndShowGUI();}});
}

使用invokeLater方法

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

Runnable updateAComponent = new Runnable() {public void run() { component.doSomething(); }
};
SwingUtilities.invokeLater(updateAComponent);

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

使用invokeAndWait方法

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

这里有一个使用invokeAndWait的例子:

void showHelloThereDialog() throws Exception {Runnable showModalDialog = new Runnable() {public void run() {JOptionPane.showMessageDialog(myMainFrame,"Hello There");}};SwingUtilities.invokeAndWait(showModalDialog);
}

类似地,一个需要获取GUI状态的线程也需要类似的处理,比如包含两个文本域的组件,可能会有如下代码:

void printTextField() throws Exception {final String[] myStrings = new String[2];Runnable getTextFieldText = new Runnable() {public void run() {myStrings[0] = textField0.getText();myStrings[1] = textField1.getText();}};SwingUtilities.invokeAndWait(getTextFieldText);System.out.println(myStrings[0] + " " + myStrings[1]);
}

使用线程提高性能

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

  • 将一个比较消耗时间的初始化任务移出主线程,可以使GUI出现得更快。耗时任务包括做额外的计算任务以及网络阻塞或硬盘I/O(比如,载入图片)。
  • 将耗时任务移出事件分发线程,从而使GUI同时继续响应用户操作。
  • 为了能重复执行一个操作,通常还需要在操作之间设置一个预置时间段(定时器)。
  • 等待其他程序的消息。

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

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

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

  • 如果当你需要更新一个组件但事件监听器中的代码并没有执行的时候,可以考虑使用这两个方法,SwingUtilities的invokeLater(优先选项)或invokeAndWait方法。
  • 如果你不能确定事件监听器中的代码 是否已经执行,那你就应当分析程序代码和线程中每个函数的调用文件。如果还是不行,可以使用SwingUtilities的 isEventDispatchThread方法。当该方法在事件分发线程中执行时返回true。你能在任何线程中安全地调用invokeLater,但 invokeAndWait会在调用线程不是事件分发线程时抛出异常。
  • 如果你需要在一段延迟之后更新组件(不论你的代码目前是否正在一个事件监听器中运行),那就使用定时器timer吧。
  • 如果你需要在没经过一段规律的时间间隔后更新组件,使用定时器timer。

使用timer定时器的信息和例子,请见如何使用Swing定时器。

使用SwingWorker类

注意:SwingWorker类的实现已经有过两次更新了,最近的一次是2000年的2月。第一次更新(在1999年1月)允许程序安全地中断工作线程。最近的一次更新(称作“SwingWorker 3”)修正了一个会引起空指针异常NullPointerException的隐藏线程bug。

类SwingWorker在SwingWorker.java中 实现,它没有在Swing包中发布。使用SwingWorker类之前,需要先创建一个SwingWorker的子类。子类必须要实现构造函数使它能包含 执行耗时操作的代码。当初始化SwingWorker子类的时候,SwingWorker类创建了一个线程但没启动它(截至SwingWorker 3)。然后才是调用SwingWorker对象的启动start方法来启动线程,就是调用构造函数。

这里有一个例子,使用SwingWorker类将耗时任务从动作事件监听器中移动到后台线程中,从而使GUI能保持响应。

//OLD CODE:
public void actionPerformed(ActionEvent e) {...//...code that might take a while to execute is here......
}//BETTER CODE:
public void actionPerformed(ActionEvent e) {...final SwingWorker worker = new SwingWorker() {public Object construct() {//...code that might take a while to execute is here...return someValue;}};worker.start();  //required for SwingWorker 3...
}

构造函数的返回值可以是任意的对象。想获取该返回值,可以调用SwingWorker对象的get函数。对于get函数要小心使用。因为它会阻塞,会引起死锁。如果有必要的话,可以调用SwingWorker的中断函数interrupt中断线程(引起函数返回)。

如果你想在耗时 任务完成的时候更新GUI,可以调用get函数(这个正如需要注意的那样,有些危险)或是重写你SwingWorker子类的finished函数。 Finished函数会在构造函数返回后执行。因为finished函数在事件分发线程中执行,你能安全地使用它更新Swing组件。当然,你最好不要把 耗时操作放进finished函数中。

下面实现finished函数的例子是从IconDemoApplet.java文件中拿来的。若想充分地讨论这个applet,包括如何使用后台线程载入图片以提高响应性能,请看如何使用Icons。

public void actionPerformed(ActionEvent e) {...if (icon == null) {     //haven't viewed this photo beforeloadImage(imagedir + pic.filename, current);} else {updatePhotograph(current, pic);}
}
...
//Load an image in a separate thread.
private void loadImage(final String imagePath, final int index) {final SwingWorker worker = new SwingWorker() {ImageIcon icon = null;public Object construct() {icon = new ImageIcon(getURL(imagePath));return icon; //return value not used by this program}//Runs on the event-dispatching thread.public void finished() {Photo pic = (Photo)pictures.elementAt(index);pic.setIcon(icon);if (index == current)updatePhotograph(index, pic);}};worker.start();
}

更多使用SwingWorker的例子,请看How to Monitor Progress。还有,TumbleItem.java,在How to Make Applets中讨论过的,既使用了SwingWorker,还使用了计时器Timer。

讨论:http://www.ime.uerj.br/javatutor/uiswing/misc/threads.html

官方解释:

public static void invokeLater(Runnable doRun)

函数public static void invokeLater(Runnable doRun)引起函数doRun.run()在AWT的事件分发线程中异步执行。这个函数应该在程序线程需要更新GUI的时候调用。下面的例子中,invokeLater调用事件分发线程的Runnable对象doHelloWorld到队列中,然后打印一条信息。

 Runnable doHelloWorld = new Runnable() {public void run() {System.out.println("Hello World on " + Thread.currentThread());}};

如果是从事件分发线程调用的invokeLater——比如,从一个JButton的监听器中——doRun.run()会一直延迟到事件队列中的所有待处理事件都处理完才 执行。要注意,当doRun.run()抛出一个未捕获的异常时,事件分发线程会释放掉(不是当前线程)。

更多关于这个函数的文件和例子请参见Java 教程中的How to Use Threads。

至于1.3部分,这个函数是由java.awt.EventQueue.invokeLater()包装而来。

不像Swing的其他函数,这个函数可以从任意线程中调用。

还可参见:

  • invokeAndWait(java.lang.Runnable)

转载于:https://www.cnblogs.com/makefile/articles/4460846.html

Java Swing线程之SwingUtilities.invokeLater解释相关推荐

  1. Java多线程之Callable、Future和FutureTask

    Java多线程之Callable接口 自己想总结一下的,看到一篇总结的更好的博客,就转载了,突然感觉真轻松,哈哈哈哈 文章转载于:Matrix海子:Java并发编程:Callable.Future和F ...

  2. Java多线程之volatile详解

    Java多线程之volatile详解 目录: 什么是volatile? JMM内存模型之可见性 volatile三大特性之一:保证可见性 volatile三大特性之二:不保证原子性 volatile三 ...

  3. JAVA多线程之wait/notify

    本文主要学习JAVA多线程中的 wait()方法 与 notify()/notifyAll()方法的用法. ①wait() 与 notify/notifyAll 方法必须在同步代码块中使用 ②wait ...

  4. Java多线程之Synchronized和Lock的区别

    Java多线程之Synchronized和Lock的区别 目录: 原始构成 使用方法 等待是否可以中断 加锁是否公平 锁绑定多个条件Condition 小结:Lock相比较Synchronized的优 ...

  5. Java多线程之CAS缺点

    Java多线程之CAS缺点 目录: 循环时间开销很大 只能保证一个共享变量的原子操作 引来ABA问题及解决方案(重点) 1. 循环时间开销很大 通过看源码,我们发现有个do while,如果CAS失败 ...

  6. Java多线程之CAS深入解析

    Java多线程之CAS深入解析 目录: CAS是什么 CAS底层原理Unsafe深入解析 CAS缺点 引子:蚂蚁花呗一面:讲一讲AtomicInteger,为什么要用CAS而不是synchronize ...

  7. Java多线程之Semaphore用法

    Java多线程之Semaphore用法 本文目录: Semaphore基本概念 Semaphore使用案例:3个停车位,6辆车去抢,走一辆,抢一个停车位. 1. Semaphore基本概念 在信号量上 ...

  8. Java多线程之CyclicBarrier用法

    Java多线程之CyclicBarrier用法 本文目录 CyclicBarrier的基本概念 CyclicBarrier的案例:集齐7颗龙珠就可以召唤神龙 1. CyclicBarrier的基本概念 ...

  9. Java多线程之CountDownLatch用法

    Java多线程之CountDownLatch用法 本文目录: CountDownLatch基本概念 CountDownLatch案例:6个同学陆续离开教室后班长才可以关门 CountDownLatch ...

  10. Java多线程之8Lock问题解析

    Java多线程之8Lock问题解析 本文目录 1. 8Lock实例: 标准访问的时候,请问先打印邮件还是短信? sendEmail方法暂停4秒钟,请问先打印邮件还是短信? 新增Hello普通方法,请问 ...

最新文章

  1. 关于烂代码的那些事(中)
  2. java sql编辑器_Java工程师完整学习路线
  3. 命令行执行php脚本中的$argv和$argc配置方法
  4. 如何查看Exchange2010中邮箱数据库的用户邮箱占用情况
  5. bash awk_Bash指南,Linux终端技巧,DevOps错误,Python,awk,NASA等
  6. pivottablejs|在Jupyter 中尽情使用数据透视表!
  7. rust爱拍视频解说_抖音视频制作必备Mac神器,上热门儿不是事儿
  8. xshell 自动断开解决方法
  9. vc 控制台添加托盘显示_VC添加托盘图标
  10. Selenium基础篇之5-第一个完整的自动化测试脚本
  11. 【气动学】基于matlab RBF神经网络控制卫星轨道和姿态【含Matlab源码 377期】
  12. google“退出”中国?
  13. Kali-WIFI攻防(一)----无线网络嗅探工具Kismet
  14. 一键实现证件照背景的替换,Python 制作可视化GUI界面真香啊
  15. 在命令窗中查询当前电脑IP
  16. oh my zsh 的alias文件
  17. 数据处理一条龙!这15个Python库不可不知
  18. python按照号段生成手机号接收验证码,「 python 」 python 实现短信验证码
  19. 用友-应收应付重分类公式设置
  20. Microsoft Visual Studio

热门文章

  1. opencv绘制矩形框
  2. 图数据集之cora数据集介绍- 用pyton处理 - 可用于GCN任务
  3. 机器学习常用算法总结
  4. 周末巨献:100+诡异的数据集,20万Eclipse Bug、死囚遗言
  5. python实现基于 Adaboost 框架来构建自定义集成模型【自定义基分类器模型】
  6. mysql java 查寻用户_mysql 查询不同用户 最新的一条记录
  7. chrome 模拟点击_详解爬虫模拟登陆的三种方法
  8. 零基础学启发式算法(1)-贪心算法(Greedy Algorithm)
  9. Linux ubuntu centos 文件目录是什么意思
  10. 数据结构和算法——八种常用的排序算法------基数排序