Swing 线程之SwingUtilities.invokeLater()
Swing 线程之SwingUtilities.invokeLater()
现在我们要做一个简单的界面。
包括一个进度条、一个输入框、开始和停止按钮。
需要实现的功能是:
当点击开始按钮,则更新进度条,并且在输入框内把完成的百分比输出(这里只做例子,没有真正去做某个工作)。
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.JTextField;
public class SwingThreadTest1 extends JFrame {
private static final long serialVersionUID = 1L;
private static final String STR = "Completed : ";
private JProgressBar progressBar = new JProgressBar();
private JTextField text = new JTextField(10);
private JButton start = new JButton("Start");
private JButton end = new JButton("End");
private boolean flag = false;
private int count = 0;
public SwingThreadTest1() {
this.setLayout(new FlowLayout());
add(progressBar);
text.setEditable(false);
add(text);
add(start);
add(end);
start.addActionListener(new Start());
end.addActionListener(new End());
}
private void go() {
while (count < 100) {
try {
Thread.sleep(100);//这里比作要完成的某个耗时的工作
} catch (InterruptedException e) {
e.printStackTrace();
}
//更新进度条和输入框
if (flag) {
count++;
progressBar.setValue(count);
text.setText(STR + String.valueOf(count) + "%");
}
}
}
private class Start implements ActionListener {
public void actionPerformed(ActionEvent e) {
flag = true;//设置开始更新的标志
go();//开始工作
}
}
private class End implements ActionListener {
public void actionPerformed(ActionEvent e) {
flag = false;//停止
}
}
public static void main(String[] args) {
SwingThreadTest1 fg = new SwingThreadTest1();
fg.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
fg.setSize(300, 100);
fg.setVisible(true);
}
}
运行代码发现,
现象1:当点击了开始按钮,画面就卡住了。按钮不能点击,进度条没有被更新,输入框上也没有任何信息。
原因分析:Swing是线程不安全的,是单线程的设计,所以只能从事件派发线程访问将要在屏幕上绘制的Swing组件。ActionListener的actionPerformed方法是在事件派发线程中调用执行的,而点击了开始按钮后,执行了go()方法,在go()里,虽然也去执行了更新组件的方法
(所谓事件派发线程,就是指事件响应线程,就是各种各样的,事件响应线程.我理解是所有的listener都在同一个线程里面注册过,然后这个线程按照先后顺序,一次执行各个listener里面的方法.)
progressBar.setValue(count);
text.setText(STR + String.valueOf(count) + "%");
但由于go()方法直到循环结束,它并没有返回,所以更新组件的操作一直没有被执行,这就造成了画面卡住的现象。
现象2:过了一段时间(go方法里的循环结束了)后,画面又可以操作,并且进度条被更新,输入框也出现了我们想看到的信息。
原因分析:通过在现象1的分析,很容易联想到,当go()方法返回了,则其他的线程(更新组件)可以被派发了,所以画面上的组件被更新了。
(by joe: 我这样解读,listener1,listener2, ...listenerN 都注册到线程1中. 事件都触发了于是就开始从listener1 开始响应,响应完了listener1,后再响应listener2, listenerN. 但是这里因为 listener1, 虽然启动了要更新界面,但是只是触发了listener2(我们假设listener2去更新界面),因为listener1 一直没有返回,所以一直轮不到 listener2执行.所以我们让listener1很快返回的话,界面就可以更新了.)
为了让画面不会卡住,我们来修改代码,将耗时的工作放在一个线程里去做。
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.JTextField;
public class SwingThreadTest2 extends JFrame {
private static final long serialVersionUID = 1L;
private static final String STR = "Completed : ";
private JProgressBar progressBar = new JProgressBar();
private JTextField text = new JTextField(10);
private JButton start = new JButton("Start");
private JButton end = new JButton("End");
private boolean flag = false;
private int count = 0;
GoThread t = null;
public SwingThreadTest2() {
this.setLayout(new FlowLayout());
add(progressBar);
text.setEditable(false);
add(text);
add(start);
add(end);
start.addActionListener(new Start());
end.addActionListener(new End());
}
private void go() {
while (count < 100) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (flag) {
count++;
System.out.println(count);
progressBar.setValue(count);
text.setText(STR + String.valueOf(count) + "%");
}
}
}
private class Start implements ActionListener {
public void actionPerformed(ActionEvent e) {
flag = true;
if(t == null){
t = new GoThread();
t.start();
}
}
}
//执行复杂工作,然后更新组件的线程
class GoThread extends Thread{
public void run() {
//do something...
go();
}
}
private class End implements ActionListener {
public void actionPerformed(ActionEvent e) {
flag = false;
}
}
public static void main(String[] args) {
SwingThreadTest2 fg = new SwingThreadTest2();
fg.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
fg.setSize(300, 100);
fg.setVisible(true);
}
}
我们执行了程序,结果和我们想要的一样,画面不会卡住了。
那这个程序是否没有问题了呢?
我们自定义了一个线程GoThread,在这里我们完成了那些耗时的工作,可以看作是“工作线程”,
而对于组件的更新,我们也放在了“工作线程”里完成了。
在这里,在事件派发线程以外的线程里设置进度条,是一个危险的操作,运行是不正常的。(对于输入框组件的更新是安全的。)
只有从事件派发线程才能更新组件,根据这个原则,我们来修改我们现有代码。
代码3:
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class SwingThreadTest3 extends JFrame {
private static final long serialVersionUID = 1L;
private static final String STR = "Completed : ";
private JProgressBar progressBar = new JProgressBar();
private JTextField text = new JTextField(10);
private JButton start = new JButton("Start");
private JButton end = new JButton("End");
private boolean flag = false;
private int count = 0;
private GoThread t = null;
private Runnable run = null;//更新组件的线程
public SwingThreadTest3() {
this.setLayout(new FlowLayout());
add(progressBar);
text.setEditable(false);
add(text);
add(start);
add(end);
start.addActionListener(new Start());
end.addActionListener(new End());
run = new Runnable(){//实例化更新组件的线程
public void run() {
progressBar.setValue(count);
text.setText(STR + String.valueOf(count) + "%");
}
};
}
private void go() {
while (count < 100) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (flag) {
count++;
SwingUtilities.invokeLater(run);//将对象排到事件派发线程的队列中
}
}
}
private class Start implements ActionListener {
public void actionPerformed(ActionEvent e) {
flag = true;
if(t == null){
t = new GoThread();
t.start();
}
}
}
class GoThread extends Thread{
public void run() {
//do something...
go();
}
}
private class End implements ActionListener {
public void actionPerformed(ActionEvent e) {
flag = false;
}
}
public static void main(String[] args) {
SwingThreadTest3 fg = new SwingThreadTest3();
fg.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
fg.setSize(300, 100);
fg.setVisible(true);
}
}
解释:SwingUtilities.invokeLater()方法使事件派发线程上的可运行对象排队。当可运行对象排在事件派发队列的队首时,就调用其run方法。其效果是允许事件派发线程调用另一个线程中的任意一个代码块。
还有一个方法SwingUtilities.invokeAndWait()方法,它也可以使事件派发线程上的可运行对象排队。
他们的不同之处在于:SwingUtilities.invokeLater()在把可运行的对象放入队列后就返回,而SwingUtilities.invokeAndWait()一直等待知道已启动了可运行的run方法才返回。如果一个操作在另外一个操作执行之前必须从一个组件获得信息,则应使用SwingUtilities.invokeAndWait()方法。
Swing 线程之SwingUtilities.invokeLater()相关推荐
- SwingUtilities.invokeLater
SwingUtilities.invokeLater(new Runnable() {@Overridepublic void run() {mainTablePane.tableRefresh(); ...
- Android多线程之ArrayBlockingQueue源码解析
阻塞队列系列 Android多线程之LinkedBlockingQueue源码解析 Android多线程之SynchronousQueue源码解析 Andorid多线程之DelayQueue源码分析 ...
- Asp.Net Core 轻松学-多线程之Task快速上手
Asp.Net Core 轻松学-多线程之Task快速上手 原文:Asp.Net Core 轻松学-多线程之Task快速上手 前言 Task是从 .NET Framework 4 开始引入的一 ...
- JAVA多线程之wait/notify
本文主要学习JAVA多线程中的 wait()方法 与 notify()/notifyAll()方法的用法. ①wait() 与 notify/notifyAll 方法必须在同步代码块中使用 ②wait ...
- linux 线程pthread_detach,linux线程之pthread_join和pthread_detach
在任何一个时间点上,线程是可结合的(joinable)或者是分离的(detached).一个可结合的线程能够被其他线程收回其资源和杀死.在 被其他线程回收之前,它的存储器资源(例如栈)是不释放的.相反 ...
- Java多线程之Callable、Future和FutureTask
Java多线程之Callable接口 自己想总结一下的,看到一篇总结的更好的博客,就转载了,突然感觉真轻松,哈哈哈哈 文章转载于:Matrix海子:Java并发编程:Callable.Future和F ...
- Java多线程之Synchronized和Lock的区别
Java多线程之Synchronized和Lock的区别 目录: 原始构成 使用方法 等待是否可以中断 加锁是否公平 锁绑定多个条件Condition 小结:Lock相比较Synchronized的优 ...
- Java多线程之CAS缺点
Java多线程之CAS缺点 目录: 循环时间开销很大 只能保证一个共享变量的原子操作 引来ABA问题及解决方案(重点) 1. 循环时间开销很大 通过看源码,我们发现有个do while,如果CAS失败 ...
- Java多线程之CAS深入解析
Java多线程之CAS深入解析 目录: CAS是什么 CAS底层原理Unsafe深入解析 CAS缺点 引子:蚂蚁花呗一面:讲一讲AtomicInteger,为什么要用CAS而不是synchronize ...
- Java多线程之volatile详解
Java多线程之volatile详解 目录: 什么是volatile? JMM内存模型之可见性 volatile三大特性之一:保证可见性 volatile三大特性之二:不保证原子性 volatile三 ...
最新文章
- python生成日历书上哪里错了_python生成日历 - osc_a5pzxo31的个人空间 - OSCHINA - 中文开源技术交流社区...
- 如何在Chrome中保存您当前的所有标签,以便以后阅读
- linux su,sudo命令
- C#实现QQ空间登录
- Android App开发实战项目之电子书架的实现(附源码 简单易懂 可直接使用)
- 2011 9 11最新过QQ游戏检测Cheat Engine(CE)搜索数据
- 互联网理财产品上周收益播报排行榜
- Linux---Apache网页优化---网页压缩
- 边沿触发 与电平触发
- python编写函数 avg(lst),参数 lst 是一个列表。函数可以返回 lst 的整数平均值,调用 avg(lst) 函数求每个学生的平均成绩。
- “沉浸式大型线下游戏”?看看这次腾讯TGC上如何玩很大!
- Apache的winnt_accept: Asynchronous AcceptEx failed问题
- [C++OpenCv] 两点距离、三点角度的计算
- 差商matlab编程,Matlab数值计算差商与插值
- desktop不可用。如果该位置位于这台电脑上,请确保设备或驱动器已连接,或者光盘已插入。如果该位置位于网络上请确保已连接到网络或Internet,然后重试。如果仍然找不到该位置,则他可能已移动或删除
- SpringCloud Module依赖程序包找不到问题解决方法
- 从RGB色转为灰度色算法
- Python123.io---星号下三角形
- MS7024 RGB/DVP转CVBS
- object-c iOS 教程 git for mac
热门文章
- 多线程基础(三)NSThread基础
- spring mvc 基础学习
- accel-pptp 部署
- Hadoop学习入门(二)——部署关键问题1:OpenSSH 密钥管理(1)
- vc++6.0如何调试
- SQLServer实战经验分享--ServiceBroker安全配置和使用示例
- 最新 Python 爬虫利器!
- Java 日期格式工具类
- bzoj1003 [ZJOI2006]物流运输
- Solr7部署报错:java.lang.NoSuchMethodError: javax.servlet.ServletInputStream.isFinished()Z