ALL is Well!

          敏捷是一條很長的路,摸索著前進(jìn)著

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

          現(xiàn)在我們要做一個(gè)簡單的界面。

          包括一個(gè)進(jìn)度條、一個(gè)輸入框、開始和停止按鈕。

          需要實(shí)現(xiàn)的功能是:

          當(dāng)點(diǎn)擊開始按鈕,則更新進(jìn)度條,并且在輸入框內(nèi)把完成的百分比輸出(這里只做例子,沒有真正去做某個(gè)工作)

          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);//這里比作要完成的某個(gè)耗時(shí)的工作   
                      }
           catch (InterruptedException e) {   
                          e.printStackTrace();   
                      }
             
                                   
          //更新進(jìn)度條和輸入框   
                      if (flag) {   
                          count
          ++;   
                          progressBar.setValue(count);   
                          text.setText(STR 
          + String.valueOf(count) + "%");   
                      }
             
                  }
             
              }
             
              
          private class Start implements ActionListener {   
                  
          public void actionPerformed(ActionEvent e) {   
                      flag 
          = true;//設(shè)置開始更新的標(biāo)志   
                      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);   
              }
             
          }

          運(yùn)行代碼發(fā)現(xiàn),

          現(xiàn)象1:當(dāng)點(diǎn)擊了開始按鈕,畫面就卡住了。按鈕不能點(diǎn)擊,進(jìn)度條沒有被更新,輸入框上也沒有任何信息。

          原因分析:Swing是線程不安全的,是單線程的設(shè)計(jì),所以只能從事件派發(fā)線程訪問將要在屏幕上繪制的Swing組件。ActionListener的actionPerformed方法是在事件派發(fā)線程中調(diào)用執(zhí)行的,而點(diǎn)擊了開始按鈕后,執(zhí)行了go()方法,在go()里,雖然也去執(zhí)行了更新組件的方法

          progressBar.setValue(count);

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

          但由于go()方法直到循環(huán)結(jié)束,它并沒有返回,所以更新組件的操作一直沒有被執(zhí)行,這就造成了畫面卡住的現(xiàn)象。

          現(xiàn)象2:過了一段時(shí)間(go方法里的循環(huán)結(jié)束了)后,畫面又可以操作,并且進(jìn)度條被更新,輸入框也出現(xiàn)了我們想看到的信息。

          原因分析:通過在現(xiàn)象1的分析,很容易聯(lián)想到,當(dāng)go()方法返回了,則其他的線程(更新組件)可以被派發(fā)了,所以畫面上的組件被更新了。

          為了讓畫面不會卡住,我們來修改代碼,將耗時(shí)的工作放在一個(gè)線程里去做。

           


          代碼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    //執(zhí)行復(fù)雜工作,然后更新組件的線程   
          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}


          我們執(zhí)行了程序,結(jié)果和我們想要的一樣,畫面不會卡住了。

          那這個(gè)程序是否沒有問題了呢?

          我們自定義了一個(gè)線程GoThread,在這里我們完成了那些耗時(shí)的工作,可以看作是“工作線程”,

          而對于組件的更新,我們也放在了“工作線程”里完成了。

          在這里,在事件派發(fā)線程以外的線程里設(shè)置進(jìn)度條,是一個(gè)危險(xiǎn)的操作,運(yùn)行是不正常的。(對于輸入框組件的更新是安全的。)

          只有從事件派發(fā)線程才能更新組件,根據(jù)這個(gè)原則,我們來修改我們現(xiàn)有代碼。

           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(){//實(shí)例化更新組件的線程   
          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);//將對象排到事件派發(fā)線程的隊(duì)列中   
          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()方法使事件派發(fā)線程上的可運(yùn)行對象排隊(duì)。當(dāng)可運(yùn)行對象排在事件派發(fā)隊(duì)列的隊(duì)首時(shí),就調(diào)用其run方法。其效果是允許事件派發(fā)線程調(diào)用另一個(gè)線程中的任意一個(gè)代碼塊。

          還有一個(gè)方法SwingUtilities.invokeAndWait()方法,它也可以使事件派發(fā)線程上的可運(yùn)行對象排隊(duì)。

          他們的不同之處在于:SwingUtilities.invokeLater()在把可運(yùn)行的對象放入隊(duì)列后就返回,而SwingUtilities.invokeAndWait()一直等待知道已啟動(dòng)了可運(yùn)行的run方法才返回。如果一個(gè)操作在另外一個(gè)操作執(zhí)行之前必須從一個(gè)組件獲得信息,則應(yīng)使用SwingUtilities.invokeAndWait()方法。

          ----2009年02月16日

          本文為原創(chuàng),歡迎轉(zhuǎn)載,轉(zhuǎn)載請注明出處BlogJava

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

          評論

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

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

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


          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 岱山县| 灵宝市| 肇东市| 都匀市| 宣化县| 临武县| 冕宁县| 平舆县| 平利县| 客服| 疏勒县| 济源市| 汤原县| 贵阳市| 五指山市| 广德县| 通化县| 宜川县| 什邡市| 左云县| 元谋县| 达拉特旗| 寿宁县| 侯马市| 樟树市| 平泉县| 宜兴市| 确山县| 宁津县| 安龙县| 綦江县| 会泽县| 敖汉旗| 达拉特旗| 太仆寺旗| 紫云| 宕昌县| 弥勒县| 平原县| 大同县| 夏河县|