Swing中的線程
步驟 1 : 三種線程
在Swing程序的開發(fā)中煮盼,需要建立3種線程的概念
初始化線程
初始化線程用于創(chuàng)建各種容器,組件并顯示他們带污,一旦創(chuàng)建并顯示僵控,初始化線程的任務(wù)就結(jié)束了。事件調(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í)行的。長耗時(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)的代碼
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());
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è)功能)