Swing 线程之SwingUtilities.invokeLater()

转自:  http://blog.csdn.net/bzwm/archive/2009/02/16/3895381.aspx

现在我们要做一个简单的界面。

包括一个进度条、一个输入框、开始和停止按钮。

需要实现的功能是:

当点击开始按钮,则更新进度条,并且在输入框内把完成的百分比输出(这里只做例子,没有真正去做某个工作)。
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()相关推荐

  1. SwingUtilities.invokeLater

    SwingUtilities.invokeLater(new Runnable() {@Overridepublic void run() {mainTablePane.tableRefresh(); ...

  2. Android多线程之ArrayBlockingQueue源码解析

    阻塞队列系列 Android多线程之LinkedBlockingQueue源码解析 Android多线程之SynchronousQueue源码解析 Andorid多线程之DelayQueue源码分析 ...

  3. Asp.Net Core 轻松学-多线程之Task快速上手

    Asp.Net Core 轻松学-多线程之Task快速上手 原文:Asp.Net Core 轻松学-多线程之Task快速上手 前言     Task是从 .NET Framework 4 开始引入的一 ...

  4. JAVA多线程之wait/notify

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

  5. linux 线程pthread_detach,linux线程之pthread_join和pthread_detach

    在任何一个时间点上,线程是可结合的(joinable)或者是分离的(detached).一个可结合的线程能够被其他线程收回其资源和杀死.在 被其他线程回收之前,它的存储器资源(例如栈)是不释放的.相反 ...

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

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

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

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

  8. Java多线程之CAS缺点

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

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

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

  10. Java多线程之volatile详解

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

最新文章

  1. python生成日历书上哪里错了_python生成日历 - osc_a5pzxo31的个人空间 - OSCHINA - 中文开源技术交流社区...
  2. 如何在Chrome中保存您当前的所有标签,以便以后阅读
  3. linux su,sudo命令
  4. C#实现QQ空间登录
  5. Android App开发实战项目之电子书架的实现(附源码 简单易懂 可直接使用)
  6. 2011 9 11最新过QQ游戏检测Cheat Engine(CE)搜索数据
  7. 互联网理财产品上周收益播报排行榜
  8. Linux---Apache网页优化---网页压缩
  9. 边沿触发 与电平触发
  10. python编写函数 avg(lst),参数 lst 是一个列表。函数可以返回 lst 的整数平均值,调用 avg(lst) 函数求每个学生的平均成绩。
  11. “沉浸式大型线下游戏”?看看这次腾讯TGC上如何玩很大!
  12. Apache的winnt_accept: Asynchronous AcceptEx failed问题
  13. [C++OpenCv] 两点距离、三点角度的计算
  14. 差商matlab编程,Matlab数值计算差商与插值
  15. desktop不可用。如果该位置位于这台电脑上,请确保设备或驱动器已连接,或者光盘已插入。如果该位置位于网络上请确保已连接到网络或Internet,然后重试。如果仍然找不到该位置,则他可能已移动或删除
  16. SpringCloud Module依赖程序包找不到问题解决方法
  17. 从RGB色转为灰度色算法
  18. Python123.io---星号下三角形
  19. MS7024 RGB/DVP转CVBS
  20. object-c iOS 教程 git for mac

热门文章

  1. 多线程基础(三)NSThread基础
  2. spring mvc 基础学习
  3. accel-pptp 部署
  4. Hadoop学习入门(二)——部署关键问题1:OpenSSH 密钥管理(1)
  5. vc++6.0如何调试
  6. SQLServer实战经验分享--ServiceBroker安全配置和使用示例
  7. 最新 Python 爬虫利器!
  8. Java 日期格式工具类
  9. bzoj1003 [ZJOI2006]物流运输
  10. Solr7部署报错:java.lang.NoSuchMethodError: javax.servlet.ServletInputStream.isFinished()Z