Java自學(xué)-圖形界面 Swing中的線程

Swing中的線程

步驟 1 : 三種線程

在Swing程序的開發(fā)中煮盼,需要建立3種線程的概念

  1. 初始化線程
    初始化線程用于創(chuàng)建各種容器,組件并顯示他們带污,一旦創(chuàng)建并顯示僵控,初始化線程的任務(wù)就結(jié)束了。

  2. 事件調(diào)度線程
    通過事件監(jiān)聽的學(xué)習(xí)鱼冀,我們了解到Swing是一個(gè)事件驅(qū)動(dòng)的模型报破,所有和事件相關(guān)的操作都放是放在事件調(diào)度線程 (Event Dispatch)中進(jìn)行的。比如點(diǎn)擊一個(gè)按鈕千绪,對(duì)應(yīng)的ActionListener.actionPerformed 方法中的代碼充易,就是在事件調(diào)度線程 Event Dispatch Thread中執(zhí)行的。

  3. 長耗時(shí)任務(wù)線程
    有時(shí)候需要進(jìn)行一些長時(shí)間的操作翘紊,比如訪問數(shù)據(jù)庫蔽氨,文件復(fù)制藐唠,連接網(wǎng)絡(luò)帆疟,統(tǒng)計(jì)文件總數(shù)等等。 這些操作就不適合放在事件調(diào)度線程中進(jìn)行宇立,因?yàn)檎加脮r(shí)間久了踪宠,會(huì)讓使用者感覺界面響應(yīng)很卡頓。 為了保持界面響應(yīng)的流暢性,所有長耗時(shí)任務(wù)都應(yīng)該放在專門的 長耗時(shí)任務(wù)線程中進(jìn)行

步驟 2 : 事件調(diào)度線程是單線程的

在開始講解這3種線程之前妈嘹, 要建立一個(gè)概念: 事件調(diào)度線程是單線程的柳琢。
為什么呢?
這是因?yàn)?Swing里面的各種組件類,比如JTextField,JButton 都不是線程安全的柬脸,這就意味著他去,如果有多個(gè)線程,那么同一個(gè)JTextField的setText方法倒堕,可能會(huì)被多個(gè)線程同時(shí)調(diào)用灾测,這會(huì)導(dǎo)致同步問題以及錯(cuò)誤數(shù)據(jù)的發(fā)生。

如果把組件類設(shè)計(jì)成為線程安全的垦巴,由于Swing事件調(diào)度的復(fù)雜性媳搪,就很有可能導(dǎo)致死鎖的發(fā)生。

為了規(guī)避同步問題骤宣,以及降低整個(gè)Swing設(shè)計(jì)的復(fù)雜度秦爆,提高Swing的相應(yīng)速度,Swing中的 事件調(diào)度線程被設(shè)計(jì)成為了單線程模式憔披,即只有一個(gè)線程在負(fù)責(zé)事件的響應(yīng)工作等限。

步驟 3 : 初始化線程

如代碼所示,同時(shí)我們?cè)诔跏蓟粋€(gè)圖形界面的時(shí)候芬膝,都會(huì)直接在主方法的主線程里精刷,直接調(diào)用如下代碼來進(jìn)行初始化

new TestFrame().setVisible(true);

如果是小程序這沒有什么問題,如果是復(fù)雜的程序就有可能產(chǎn)生問題了蔗候。因?yàn)檫@里有兩個(gè)線程在同時(shí)訪問組件:1. 主線程 2. 事件調(diào)度線程怒允。 如果是復(fù)雜的圖形界面程序,就有可能出現(xiàn)這兩個(gè)線程同時(shí)操作的情況锈遥,導(dǎo)致同步問題的產(chǎn)生纫事。

為了規(guī)避這個(gè)問題的產(chǎn)生,創(chuàng)建和顯示界面的工作所灸,最好也交給事件調(diào)度線程丽惶,這樣就保證了只有一個(gè)線程在訪問這些組件

SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        new TestFrame().setVisible(true);
    }
});

像這樣,new TestFrame().setVisible(true); 這段代碼就是在事件調(diào)度線程中執(zhí)行了爬立。

還可以使用SwingUtilities.isEventDispatchThread()來判斷當(dāng)前線程是否是事件調(diào)度線程

package gui;
 
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
 
public class TestGUI {
    public static void main(String[] args) {
        new TestFrame().setVisible(true);
         
//      SwingUtilities.invokeLater(new Runnable() {
//          public void run() {
//              new TestFrame().setVisible(true);
//          }
//      });
    }
 
    static class TestFrame extends JFrame {
        public TestFrame() {
            setTitle("LoL");
 
            setSize(400, 300);
 
            setLocation(200, 200);
 
            setLayout(null);
 
            JButton b = new JButton("一鍵秒對(duì)方基地掛");
 
            b.setBounds(50, 50, 280, 30);
 
            add(b);
 
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
            setVisible(true);
             
            System.out.println("當(dāng)前線程是否是 事件調(diào)度線程: " + SwingUtilities.isEventDispatchThread());
 
        }
    }
}

步驟 4 : 事件調(diào)度線程

以 按鈕監(jiān)聽 中的代碼為例钾唬,ActionListener.actionPerformed 中的代碼,就是事件調(diào)度線程執(zhí)行的侠驯。

可以借助SwingUtilities.isEventDispatchThread() 確認(rèn)抡秆,是事件調(diào)度線程在執(zhí)行相應(yīng)的代碼

事件調(diào)度線程
package gui;
   
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
   
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
   
public class TestGUI {
    public static void main(String[] args) {
   
        JFrame f = new JFrame("LoL");
        f.setSize(400, 300);
        f.setLocation(580, 200);
        f.setLayout(null);
   
        final JLabel l = new JLabel();
   
        ImageIcon i = new ImageIcon("e:/project/j2se/shana.png");
        l.setIcon(i);
        l.setBounds(50, 50, i.getIconWidth(), i.getIconHeight());
   
        JButton b = new JButton("隱藏圖片");
        b.setBounds(150, 200, 100, 30);
   
        b.addActionListener(new ActionListener() {
   
            public void actionPerformed(ActionEvent e) {
                l.setVisible(false);
                 
                System.out.println("當(dāng)前使用的是事件調(diào)度線程:" + SwingUtilities.isEventDispatchThread());
            }
        });
   
        f.add(l);
        f.add(b);
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   
        f.setVisible(true);
    }
}

步驟 5 : 長耗時(shí)任務(wù)線程

有時(shí)候需要執(zhí)行長耗時(shí)任務(wù),比如數(shù)據(jù)庫查詢吟策,文件復(fù)制儒士,訪問網(wǎng)絡(luò)等等。

而這些操作一般都會(huì)在事件響應(yīng)后發(fā)起檩坚,就會(huì)自動(dòng)進(jìn)入事件調(diào)度線程着撩。 而事件調(diào)度線程又是單線程模式诅福,其結(jié)果就會(huì)是在執(zhí)行這些長耗時(shí)任務(wù)的時(shí)候,界面就無響應(yīng)了拖叙。

如圖所示氓润,當(dāng)點(diǎn)擊第一個(gè)按鈕的時(shí)候,會(huì)在其中進(jìn)行一個(gè)5秒鐘的任務(wù)薯鳍,這個(gè)期間旺芽,第一個(gè)按鈕會(huì)保持按下狀態(tài),其他按鈕也無法點(diǎn)擊辐啄,出現(xiàn)了無響應(yīng)了狀態(tài)采章。

為了解決這個(gè)問題,Swing提供了一個(gè)SwingWorker類來解決壶辜。 SwingWorker是一個(gè)抽象類悯舟,為了使用,必須實(shí)現(xiàn)方法 doInBackground砸民,在doInBackground中抵怎,就可以編寫我們的任務(wù),然后執(zhí)行SwingWorker的execute方法岭参,放在專門的工作線程中去運(yùn)行反惕。

SwingWorker worker = new SwingWorker() {
    protected Object doInBackground() throws Exception {
        //長耗時(shí)任務(wù)
        return null;
    }
};
worker.execute();

SwingWorker又是如何工作的呢?
當(dāng)SwingWorker執(zhí)行execute的時(shí)候演侯,調(diào)用默認(rèn)有10根線程的線程池姿染,執(zhí)行doInBackground中的代碼,通過如下代碼秒际,可以獲知執(zhí)行當(dāng)前SwingWorder的線程名稱

System.out.println("執(zhí)行這個(gè)SwingWorder的線程是:" + Thread.currentThread().getName());
長耗時(shí)任務(wù)線程
package gui;
 
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.JLabel;
import javax.swing.SwingWorker;
 
public class TestGUI {
    public static void main(String[] args) {
 
        JFrame f = new JFrame("LoL");
        f.setSize(300, 300);
        f.setLocation(200, 200);
        f.setLayout(new FlowLayout());
 
        JButton b1 = new JButton("在事件調(diào)度線程中執(zhí)行長耗時(shí)任務(wù)");
        JButton b2 = new JButton("使用SwingWorker執(zhí)行長耗時(shí)任務(wù)");
        JLabel l = new JLabel("任務(wù)執(zhí)行結(jié)果");
        f.add(b1);
        f.add(b2);
        f.add(l);
 
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 
        b1.addActionListener(new ActionListener() {
 
            @Override
            public void actionPerformed(ActionEvent e) {
                l.setText("開始執(zhí)行完畢");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                }
                l.setText("任務(wù)執(zhí)行完畢");
            }
        });
        b2.addActionListener(new ActionListener() {
 
            @Override
            public void actionPerformed(ActionEvent e) {
                SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
 
                    @Override
                    protected Void doInBackground() throws Exception {
                        System.out.println("執(zhí)行這個(gè)SwingWorder的線程是:" + Thread.currentThread().getName());
                        l.setText("開始執(zhí)行完畢");
                        try {
                            Thread.sleep(5000);
                        } catch (InterruptedException e1) {
                            // TODO Auto-generated catch block
                            e1.printStackTrace();
                        }
                        l.setText("任務(wù)執(zhí)行完畢");
                        return null;
                    }
                };
                worker.execute();
 
            }
        });
 
        f.setVisible(true);
    }
}

練習(xí)查找文件內(nèi)容

(查找文件內(nèi)容本身是一個(gè)比較耗時(shí)的任務(wù)悬赏,采用長耗時(shí)任務(wù)線程的手段,開發(fā)這個(gè)功能)


在這里插入圖片描述
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末娄徊,一起剝皮案震驚了整個(gè)濱河市闽颇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌寄锐,老刑警劉巖兵多,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異橄仆,居然都是意外死亡剩膘,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門沿癞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來援雇,“玉大人,你說我怎么就攤上這事椎扬”共” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵蚕涤,是天一觀的道長筐赔。 經(jīng)常有香客問我,道長揖铜,這世上最難降的妖魔是什么茴丰? 我笑而不...
    開封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮天吓,結(jié)果婚禮上贿肩,老公的妹妹穿的比我還像新娘。我一直安慰自己龄寞,他們只是感情好汰规,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著物邑,像睡著了一般溜哮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上色解,一...
    開封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天茂嗓,我揣著相機(jī)與錄音,去河邊找鬼科阎。 笑死述吸,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的锣笨。 我是一名探鬼主播刚梭,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼票唆!你這毒婦竟也來了朴读?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤走趋,失蹤者是張志新(化名)和其女友劉穎衅金,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體簿煌,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡氮唯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了姨伟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片惩琉。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖夺荒,靈堂內(nèi)的尸體忽然破棺而出瞒渠,到底是詐尸還是另有隱情良蒸,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布伍玖,位于F島的核電站嫩痰,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏窍箍。R本人自食惡果不足惜串纺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望椰棘。 院中可真熱鬧纺棺,春花似錦、人聲如沸邪狞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽外恕。三九已至杆逗,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鳞疲,已是汗流浹背罪郊。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留尚洽,地道東北人悔橄。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像腺毫,于是被迫代替她去往敵國和親癣疟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355