- Java使用Thread類(lèi)代表線程,所有的線程對(duì)象都必須是Tread類(lèi)或其子類(lèi)的實(shí)例。每條線程的作用是完成一定的任務(wù),實(shí)際上就是執(zhí)行一段程序流。Java使用run方法來(lái)封裝這樣的一段程序流。
通過(guò)繼承Thread類(lèi)來(lái)創(chuàng)建并啟動(dòng)多線程的步驟如下:
- 定義Thread類(lèi)的子類(lèi),并重寫(xiě)該類(lèi)的run方法,該run方法的方法體就是代表了線程需要完成的任務(wù)。因此我們經(jīng)常把run方法稱(chēng)為線程執(zhí)行體。
- 創(chuàng)建Thread子類(lèi)的實(shí)例,即使創(chuàng)建了線程對(duì)象
- 用線程對(duì)象的start方法來(lái)啟動(dòng)該線程
public class FirstThread extends Thread{
private int i;
public void run(){
for(;i < 100; i ++){
//當(dāng)線程類(lèi)繼承Thread類(lèi)時(shí),可以直接調(diào)用getName()方法來(lái)返回當(dāng)前線程的名字
//如果想獲取當(dāng)前線程,直接使用this即可
//Thread對(duì)象的getName()返回該線程的名字
System.out.println(getName() + "" + i);
}
}
public static void main(){
for(int i = 0; I < 100; i++){
System.out.println(Thread.currentThread().getName() + "" + i);
if (i == 20){
new FirstTread().start();
new FirstTread().start();
}
}
}
}
Thread.JPG
注意一:Thread-0 和Thread -1兩條線程輸出的i變量不連續(xù),i變量是FirstThread的實(shí)例屬性,而不是局部變量,但是因?yàn)槌绦蛎看蝿?chuàng)建線程對(duì)象時(shí)都需要?jiǎng)?chuàng)建一個(gè)FirstThread對(duì)象,所以不共享該實(shí)例屬性。使用繼承Thread類(lèi)的方法來(lái)創(chuàng)建線程類(lèi),多條線程之間無(wú)法共享線程類(lèi)的實(shí)例變量。
注意二:?jiǎn)?dòng)線程應(yīng)該使用start方法,而不是run方法。永遠(yuǎn)不要調(diào)用線程對(duì)象的run方法。調(diào)用start方法來(lái)啟動(dòng)線程,系統(tǒng)會(huì)把該run方法當(dāng)成線程執(zhí)行體來(lái)處理。但如果直接調(diào)用線程對(duì)象的run方法,系統(tǒng)指揮把線程對(duì)象當(dāng)作一個(gè)普通的對(duì)象,而run方法也只是普通方法,而不是線程執(zhí)行體。
package com.imooc;
public class MyThread extends Thread{
private int i;
public void run(){
for (; i < 100; i++){
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
public static void main(String[] args){
for (int i = 0; i < 100; i++){
System.out.println(Thread.currentThread().getName() + " " + i);
if (i ==20){
new MyThread().run();
new MyThread().run();
}
}
}
}
- 實(shí)現(xiàn)Runnable接口創(chuàng)建線程
創(chuàng)建Runnable對(duì)象作為線程對(duì)象的target。使用這種方法時(shí),兩條線程的i變量是連續(xù)的,因?yàn)閮蓷l線程使用的是同一個(gè)target,即同一個(gè)Runnable對(duì)象。
public class SecondThread implements Runnable {
private int i;
@Override
public void run() {
// TODO Auto-generated method stub
for(; i < 100; i ++){
System.out.println(Thread.currentThread().getName() + " " + i); //注意三
}
}
public static void main(String[] args){
for(int i = 0; i < 100; i++){
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 20){
SecondThread st = new SecondThread();
new Thread(st, "新線程1").start();
new Thread(st, "新線程2").start();
}
}
}
}
注意三:
使用Runnable接口時(shí),必須使用Thread.currentThread.getName()方法來(lái)查看當(dāng)前線程的名字
- 線程的生命周期
- 新建:new關(guān)鍵字創(chuàng)建了一個(gè)線程后,該線程就處于新建狀態(tài)。此時(shí)和其他Java對(duì)象一樣,僅僅由虛擬機(jī)為其分配了內(nèi)存,并初始化了成員變量的值。沒(méi)有表現(xiàn)出任何線程的動(dòng)態(tài)特征。
- 就緒:當(dāng)線程對(duì)象調(diào)用了
start()方法后,該線程就處于就緒狀態(tài),Java虛擬機(jī)會(huì)為其創(chuàng)建方法調(diào)用棧,程序計(jì)數(shù)器,但是此時(shí)還沒(méi)有運(yùn)行,只是代表著該線程可以運(yùn)行了。至于何時(shí)運(yùn)行,取決于JVM里線程調(diào)度器的調(diào)度。 - 運(yùn)行:如果就緒的線程獲得了CPU,開(kāi)始執(zhí)行
run方法的線程執(zhí)行體,此時(shí)線程處于運(yùn)行狀態(tài) - 阻塞:
發(fā)生以下情況時(shí),線程進(jìn)入阻塞:
- 線程調(diào)用sleep方法主動(dòng)放棄處理器資源
- 等待資源時(shí)
- 程序調(diào)用了線程的suspend方法將該線程掛起(容易導(dǎo)致死鎖)
針對(duì)以上的情況,發(fā)生特定的情況將可以接觸上面的阻塞,讓該線程重新進(jìn)入就緒狀態(tài):
- 調(diào)用的sleep方法經(jīng)過(guò)了指定的時(shí)間
- 等待的資源已獲得
- 處于掛起狀態(tài)的線程被調(diào)用了resume()回復(fù)方法。
- 死亡:我們可以通過(guò)調(diào)用線程對(duì)象的
isAlive()方法來(lái)檢測(cè)線程是否死亡,當(dāng)線程處于就緒,運(yùn)行,阻塞時(shí),該方法返回true;處于新建,死亡時(shí),該方法返回false。注意,不要試圖對(duì)一個(gè)已經(jīng)死亡的線程調(diào)用start()方法,否則會(huì)產(chǎn)生IllegalThreadStateException異常。
線程會(huì)以以下三種方式之一結(jié)束,結(jié)束后就處于死亡狀態(tài);
-
run()方法執(zhí)行完成,線程正常結(jié)束 - 線程拋出一個(gè)未捕獲的Exception或Error
- 直接調(diào)用該線程的
stop()來(lái)結(jié)束該線程-該方法容易導(dǎo)致死鎖 - 控制線程
- join線程:當(dāng)在某個(gè)程序執(zhí)行流中調(diào)用A線程的
join()方法時(shí),調(diào)用線程將被阻塞,直到被join()方法加入的A線程完成為止。 - 后臺(tái)線程:這種線程在后臺(tái)運(yùn)行,他的任務(wù)是為其他的線程提供服務(wù)。如果所有的前臺(tái)線程死亡,后臺(tái)線程會(huì)自動(dòng)死亡。調(diào)用Thread對(duì)象的
setDaemon(true)方法可將指定線程設(shè)置成后臺(tái)線程,但是注意,setDeamon(true)必須在start()方法前調(diào)用,否則IllegalThreadStateException異常。 - 線程睡眠sleep()方法:通過(guò)調(diào)用Thread類(lèi)的靜sleep方法,可以讓當(dāng)前正在執(zhí)行的線程暫定指定的時(shí)間,并進(jìn)入阻塞狀態(tài)
Thread.sleep(1000);,在之后,線程自動(dòng)進(jìn)入就緒狀態(tài)。 - 線程讓步y(tǒng)ield方法:這個(gè)方法也是Thread提供的一個(gè)靜態(tài)方法,它將正在執(zhí)行的線程自動(dòng)轉(zhuǎn)入就緒狀態(tài)(但不阻塞)。當(dāng)某個(gè)線程調(diào)用了yield方法暫停后,線程調(diào)度器完全可能再次將其調(diào)用出來(lái)執(zhí)行。但是,當(dāng)某個(gè)線程調(diào)用了
yield()方法暫停后,只有優(yōu)先級(jí)大于、等于當(dāng)前線程的就緒狀態(tài)的線程才會(huì)獲得執(zhí)行的機(jī)會(huì)。 - 改變線程的優(yōu)先級(jí):`thread.setPriority(MAX_PRIORITY)