ALL is Well!

          敏捷是一條很長的路,摸索著前進著

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            30 隨筆 :: 23 文章 :: 71 評論 :: 0 Trackbacks

          現在我們要做一個簡單的界面。

          包括一個進度條、一個輸入框、開始和停止按鈕。

          需要實現的功能是:

          當點擊開始按鈕,則更新進度條,并且在輸入框內把完成的百分比輸出(這里只做例子,沒有真正去做某個工作)。

          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()方法。

          ----2009年02月16日

          本文為原創,歡迎轉載,轉載請注明出處BlogJava

          posted on 2010-09-01 12:05 李 明 閱讀(1038) 評論(3)  編輯  收藏 所屬分類: J2SE 、Java

          評論

          # re: Swing 線程之SwingUtilities.invokeLater() [未登錄] 2011-02-24 16:31 llq
          樓主寫得很好,我是一名初學者,這個問題困擾了我一段時間,看了你的文章豁然開朗,謝謝  回復  更多評論
            

          # re: Swing 線程之SwingUtilities.invokeLater() 2011-02-28 11:07 Ronaldo
          @llq
          這博文我在CSDN早就發過了。  回復  更多評論
            

          # re: Swing 線程之SwingUtilities.invokeLater() 2011-09-05 12:26 sysu_mjc
          博主,"為了讓畫面不會卡住,我們來修改代碼,將耗時的工作放在一個線程里去做。"為什么這樣子就不會卡住呢,懇請解答  回復  更多評論
            

          主站蜘蛛池模板: 江山市| 开封县| 双牌县| 兴宁市| 临澧县| 霍林郭勒市| 紫阳县| 盖州市| 青川县| 娄底市| 双桥区| 瑞金市| 长治市| 夹江县| 义马市| 牙克石市| 神木县| 青岛市| 金沙县| 德州市| 巴林左旗| 陆良县| 武义县| 晋宁县| 东丽区| 县级市| 吴江市| 平利县| 江阴市| 新乡市| 镇安县| 赞皇县| 巴马| 阳高县| 万盛区| 罗山县| 荆门市| 汨罗市| 奉贤区| 盐津县| 青州市|