Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 301|回复: 0

[Swing学习]Swing线程之SwingUtilities_invokeLater()方法

[复制链接]
  • TA的每日心情
    开心
    2021-3-12 23:18
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2014-11-7 23:56:57 | 显示全部楼层 |阅读模式
    现在我们要做一个简单的界面。包括一个进度条、一个输入框、开始和停止按钮。 需要实现的功能是:
    当点击开始按钮,则更新进度条,并且在输入框内把完成的百分比输出(这里只做例子,没有真正去做某个工作)。
    1. import java.awt.FlowLayout;   
    2. import java.awt.event.ActionEvent;   
    3. import java.awt.event.ActionListener;   
    4. import javax.swing.JButton;   
    5. import javax.swing.JFrame;   
    6. import javax.swing.JProgressBar;   
    7. import javax.swing.JTextField;   
    8. public class SwingThreadTest1 extends JFrame {   
    9.     private static final long serialVersionUID = 1L;   
    10.     private static final String STR = "Completed : ";   
    11.     private JProgressBar progressBar = new JProgressBar();   
    12.     private JTextField text = new JTextField(10);   
    13.     private JButton start = new JButton("Start");   
    14.     private JButton end = new JButton("End");   
    15.     private boolean flag = false;   
    16.     private int count = 0;   
    17.     public SwingThreadTest1() {   
    18.         this.setLayout(new FlowLayout());   
    19.         add(progressBar);   
    20.         text.setEditable(false);   
    21.         add(text);   
    22.         add(start);   
    23.         add(end);   
    24.         start.addActionListener(new Start());   
    25.         end.addActionListener(new End());   
    26.     }   
    27.            
    28.     private void go() {   
    29.         while (count < 100) {   
    30.             try {   
    31.                 Thread.sleep(100);//这里比作要完成的某个耗时的工作   
    32.             } catch (InterruptedException e) {   
    33.                 e.printStackTrace();   
    34.             }   
    35.                          //更新进度条和输入框   
    36.             if (flag) {   
    37.                 count++;   
    38.                 progressBar.setValue(count);   
    39.                 text.setText(STR + String.valueOf(count) + "%");   
    40.             }   
    41.         }   
    42.     }   
    43.     private class Start implements ActionListener {   
    44.         public void actionPerformed(ActionEvent e) {   
    45.             flag = true;//设置开始更新的标志   
    46.             go();//开始工作   
    47.         }   
    48.     }   
    49.     private class End implements ActionListener {   
    50.         public void actionPerformed(ActionEvent e) {   
    51.             flag = false;//停止   
    52.         }   
    53.     }   
    54.     public static void main(String[] args) {   
    55.         SwingThreadTest1 fg = new SwingThreadTest1();   
    56.         fg.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);   
    57.         fg.setSize(300, 100);   
    58.         fg.setVisible(true);   
    59.     }   
    60. }
    复制代码
    运行代码发现, 现象1:当点击了开始按钮,画面就卡住了。按钮不能点击,进度条没有被更新,输入框上也没有任何信息。 原因分析:Swing是线程不安全的,是单线程的设计,所以只能从事件派发线程访问将要在屏幕上绘制的Swing组件。ActionListener的actionPerformed方法是在事件派发线程中调用执行的,而点击了开始按钮后,执行了go()方法,在go()里,虽然也去执行了更新组件的方法 progressBar.setValue(count);
    text.setText(STR + String.valueOf(count) + "%"); 但由于go()方法直到循环结束,它并没有返回,所以更新组件的操作一直没有被执行,这就造成了画面卡住的现象。 现象2:过了一段时间(go方法里的循环结束了)后,画面又可以操作,并且进度条被更新,输入框也出现了我们想看到的信息。 原因分析:通过在现象1的分析,很容易联想到,当go()方法返回了,则其他的线程(更新组件)可以被派发了,所以画面上的组件被更新了。 为了让画面不会卡住,我们来修改代码,将耗时的工作放在一个线程里去做。 代码2:
    1. import java.awt.FlowLayout;   
    2. import java.awt.event.ActionEvent;   
    3. import java.awt.event.ActionListener;   
    4. import javax.swing.JButton;   
    5. import javax.swing.JFrame;   
    6. import javax.swing.JProgressBar;   
    7. import javax.swing.JTextField;   
    8. public class SwingThreadTest2 extends JFrame {   
    9.     private static final long serialVersionUID = 1L;   
    10.     private static final String STR = "Completed : ";   
    11.     private JProgressBar progressBar = new JProgressBar();   
    12.     private JTextField text = new JTextField(10);   
    13.     private JButton start = new JButton("Start");   
    14.     private JButton end = new JButton("End");   
    15.     private boolean flag = false;   
    16.     private int count = 0;   
    17.       
    18.     GoThread t = null;   
    19.     public SwingThreadTest2() {   
    20.         this.setLayout(new FlowLayout());   
    21.         add(progressBar);   
    22.         text.setEditable(false);   
    23.         add(text);   
    24.         add(start);   
    25.         add(end);   
    26.         start.addActionListener(new Start());   
    27.         end.addActionListener(new End());   
    28.     }   
    29.     private void go() {   
    30.         while (count < 100) {   
    31.             try {   
    32.                 Thread.sleep(100);   
    33.             } catch (InterruptedException e) {   
    34.                 e.printStackTrace();   
    35.             }   
    36.             if (flag) {   
    37.                 count++;   
    38.                 System.out.println(count);   
    39.                 progressBar.setValue(count);   
    40.                 text.setText(STR + String.valueOf(count) + "%");   
    41.             }   
    42.         }   
    43.     }   
    44.     private class Start implements ActionListener {   
    45.         public void actionPerformed(ActionEvent e) {   
    46.             flag = true;   
    47.             if(t == null){   
    48.                 t = new GoThread();   
    49.                 t.start();   
    50.             }   
    51.         }   
    52.     }   
    53.     //执行复杂工作,然后更新组件的线程   
    54.     class GoThread extends Thread{   
    55.         public void run() {   
    56.             //do something   
    57.             go();   
    58.         }   
    59.     }   
    60.     private class End implements ActionListener {   
    61.         public void actionPerformed(ActionEvent e) {   
    62.             flag = false;   
    63.         }   
    64.     }   
    65.     public static void main(String[] args) {   
    66.         SwingThreadTest2 fg = new SwingThreadTest2();   
    67.         fg.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);   
    68.         fg.setSize(300, 100);   
    69.         fg.setVisible(true);   
    70.     }   
    71. }
    复制代码
    我们执行了程序,结果和我们想要的一样,画面不会卡住了。 那这个程序是否没有问题了呢? 我们自定义了一个线程GoThread,在这里我们完成了那些耗时的工作,可以看作是“工作线程”,而对于组件的更新,我们也放在了“工作线程”里完成了。在这里,在事件派发线程以外的线程里设置进度条,是一个危险的操作,运行是不正常的。(对于输入框组件的更新是安全的。) 只有从事件派发线程才能更新组件,根据这个原则,我们来修改我们现有代码。
    1. import java.awt.FlowLayout;   
    2. import java.awt.event.ActionEvent;   
    3. import java.awt.event.ActionListener;   
    4. import javax.swing.JButton;   
    5. import javax.swing.JFrame;   
    6. import javax.swing.JProgressBar;   
    7. import javax.swing.JTextField;   
    8. import javax.swing.SwingUtilities;   
    9. public class SwingThreadTest3 extends JFrame {   
    10.     private static final long serialVersionUID = 1L;   
    11.     private static final String STR = "Completed : ";   
    12.     private JProgressBar progressBar = new JProgressBar();   
    13.     private JTextField text = new JTextField(10);   
    14.    private JButton start = new JButton("Start");   
    15.     private JButton end = new JButton("End");   
    16.     private boolean flag = false;   
    17.     private int count = 0;   
    18.       
    19.     private GoThread t = null;   
    20.       
    21.     private Runnable run = null;//更新组件的线程   
    22.     public SwingThreadTest3() {   
    23.         this.setLayout(new FlowLayout());   
    24.         add(progressBar);   
    25.         text.setEditable(false);   
    26.         add(text);   
    27.         add(start);   
    28.         add(end);   
    29.         start.addActionListener(new Start());   
    30.        end.addActionListener(new End());   
    31.            
    32.         run = new Runnable(){//实例化更新组件的线程   
    33.             public void run() {   
    34.                 progressBar.setValue(count);   
    35.                 text.setText(STR + String.valueOf(count) + "%");   
    36.            }   
    37.         };   
    38.     }   
    39.     private void go() {   
    40.         while (count < 100) {   
    41.             try {   
    42.                 Thread.sleep(100);   
    43.             } catch (InterruptedException e) {   
    44.                 e.printStackTrace();   
    45.             }   
    46.             if (flag) {   
    47.                 count++;   
    48.                 SwingUtilities.invokeLater(run);//将对象排到事件派发线程的队列中   
    49.            }   
    50.         }   
    51.     }   
    52.     private class Start implements ActionListener {   
    53.         public void actionPerformed(ActionEvent e) {   
    54.             flag = true;   
    55.             if(t == null){   
    56.                 t = new GoThread();   
    57.                 t.start();   
    58.             }   
    59.         }   
    60.     }   
    61.       
    62.     class GoThread extends Thread{   
    63.         public void run() {   
    64.             //do something   
    65.             go();   
    66.         }   
    67.     }   
    68.     private class End implements ActionListener {   
    69.         public void actionPerformed(ActionEvent e) {   
    70.             flag = false;   
    71.         }   
    72.     }   
    73.     public static void main(String[] args) {   
    74.         SwingThreadTest3 fg = new SwingThreadTest3();   
    75.         fg.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);   
    76.         fg.setSize(300, 100);   
    77.         fg.setVisible(true);   
    78.     }   
    79. }
    复制代码
    解释:SwingUtilities.invokeLater()方法使事件派发线程上的可运行对象排队。当可运行对象排在事件派发队列的队首时,就调用其run方法。其效果是允许事件派发线程调用另一个线程中的任意一个代码块。 还有一个方法SwingUtilities.invokeAndWait()方法,它也可以使事件派发线程上的可运行对象排队。 他们的不同之处在于:SwingUtilities.invokeLater()在把可运行的对象放入队列后就返回,而SwingUtilities.invokeAndWait()一直等待知道已启动了可运行的run方法才返回。如果一个操作在另外一个操作执行之前必须从一个组件获得信息,则应使用SwingUtilities.invokeAndWait()方法。  
       
         
         
          
          

            
          

            
          
         
       

      


    源码下载:http://file.javaxxz.com/2014/11/7/235657156.zip
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-2-25 10:14 , Processed in 0.299662 second(s), 34 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表