• 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(
    300100);   
            fg.setVisible(
    true);   
        }
       
    }

    运行代码发现,

    现象1:当点击了开始按钮,画面就卡住了。按钮不能点击,进度条没有被更新,输入框上也没有任何信息。

    原因分析:Swing是线程不安全的,是单线程的设计,所以只能从事件派发线程访问将要在屏幕上绘制的Swing组件。ActionListener的actionPerformed方法是在事件派发线程中调用执行的,而点击了开始按钮后,执行了go()方法,在go()里,虽然也去执行了更新组件的方法

    progressBar.setValue(count);

    text.setText(STR + String.valueOf(count) + "%");

    但由于go()方法直到循环结束,它并没有返回,所以更新组件的操作一直没有被执行,这就造成了画面卡住的现象。

    现象2:过了一段时间(go方法里的循环结束了)后,画面又可以操作,并且进度条被更新,输入框也出现了我们想看到的信息。

    原因分析:通过在现象1的分析,很容易联想到,当go()方法返回了,则其他的线程(更新组件)可以被派发了,所以画面上的组件被更新了。

    为了让画面不会卡住,我们来修改代码,将耗时的工作放在一个线程里去做。

     


    代码2:

     

     1import java.awt.FlowLayout;   
     2import java.awt.event.ActionEvent;   
     3import java.awt.event.ActionListener;   
     4import javax.swing.JButton;   
     5import javax.swing.JFrame;   
     6import javax.swing.JProgressBar;   
     7import javax.swing.JTextField;   
     8public 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(300100);   
    69        fg.setVisible(true);   
    70    }
       
    71}


    我们执行了程序,结果和我们想要的一样,画面不会卡住了。

    那这个程序是否没有问题了呢?

    我们自定义了一个线程GoThread,在这里我们完成了那些耗时的工作,可以看作是“工作线程”,

    而对于组件的更新,我们也放在了“工作线程”里完成了。

    在这里,在事件派发线程以外的线程里设置进度条,是一个危险的操作,运行是不正常的。(对于输入框组件的更新是安全的。)

    只有从事件派发线程才能更新组件,根据这个原则,我们来修改我们现有代码。

     1import java.awt.FlowLayout;   
     2import java.awt.event.ActionEvent;   
     3import java.awt.event.ActionListener;   
     4import javax.swing.JButton;   
     5import javax.swing.JFrame;   
     6import javax.swing.JProgressBar;   
     7import javax.swing.JTextField;   
     8import javax.swing.SwingUtilities;   
     9public 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(300100);   
    77        fg.setVisible(true);   
    78    }
       
    79}


    解释:SwingUtilities.invokeLater()方法使事件派发线程上的可运行对象排队。当可运行对象排在事件派发队列的队首时,就调用其run方法。其效果是允许事件派发线程调用另一个线程中的任意一个代码块。

    还有一个方法SwingUtilities.invokeAndWait()方法,它也可以使事件派发线程上的可运行对象排队。

    他们的不同之处在于:SwingUtilities.invokeLater()在把可运行的对象放入队列后就返回,而SwingUtilities.invokeAndWait()一直等待知道已启动了可运行的run方法才返回。如果一个操作在另外一个操作执行之前必须从一个组件获得信息,则应使用SwingUtilities.invokeAndWait()方法。

  • 相关阅读:
    client-go和golang源码中的技巧
    使用Prometheus监控snmp
    kubernetes client-go解析
    go 语言的一个赋值操作
    使用Prometheus监控bind9的DNS服务
    验证Prometheus alertmanager邮件发送
    JS 监听浏览器各个标签间的切换
    通过案例理解position:relative和position:absolute
    浏览器的统一指针事件:Pointer Event
    Chrome浏览器读写系统剪切板
  • 原文地址:https://www.cnblogs.com/daichangya/p/12959317.html
Copyright © 2020-2023  润新知