下面將會針這張圖對線程的狀態(tài)轉(zhuǎn)換來做解釋

在操作系統(tǒng)的課程中把線程大致分為了3個(gè)狀態(tài)
1.就緒狀態(tài)(線程對資源上鎖,但未分配時(shí)間片,等待cpu分配,隨時(shí)可以運(yùn)行)
2.運(yùn)行狀態(tài)(線程對資源上鎖,并擁有了時(shí)間片,正在運(yùn)行中)
3.阻塞狀態(tài)(在運(yùn)行狀態(tài)下缺失了某種資源導(dǎo)致運(yùn)行暫停,也可以是被cpu強(qiáng)制暫停)
java中多了新建和死亡狀態(tài),新建狀態(tài)(new)就是線程定義好了之后,還沒調(diào)用start方法告訴操作系統(tǒng)。死亡狀態(tài)就是run方法執(zhí)行完,也就是線程操作完畢。這兩個(gè)狀態(tài)很容易理解。
就緒----->運(yùn)行很簡單,分配時(shí)間片就行,這個(gè)不需要我們擔(dān)心,
運(yùn)行----->就緒就不簡單了,因?yàn)橹虚g可能存在阻塞態(tài)
(1)運(yùn)行----->就緒
這個(gè)狀態(tài)變換是由于線程失去時(shí)間片,可以使用.yield()實(shí)現(xiàn)

使用yield()后線程短暫停,暫停由系統(tǒng)決定的毫秒級時(shí)間,線程不釋放鎖,僅剝奪時(shí)間片,所以線程暫停結(jié)束后進(jìn)入就緒狀態(tài)
(2)運(yùn)行----->阻塞------>就緒
1)不釋放鎖的阻塞
包括sleep,join方法
sleep()方法

我們看到sleep()方法描述暫停一定時(shí)間后恢復(fù)就緒狀態(tài),不釋放鎖,時(shí)間結(jié)束后進(jìn)入就緒狀態(tài)。跟yield很像,那為什么sleep算阻塞呢,原因是sleep()的暫停時(shí)間是系統(tǒng)和用戶一起決定的,用戶可以設(shè)定休眠很長時(shí)間,比如一分鐘,我們知道線程的運(yùn)行都是毫秒級別的,這樣一比sleep自然就屬于了阻塞。
join()
這個(gè)方法讓調(diào)用該方法的線程優(yōu)先執(zhí)行,通常設(shè)置在另一個(gè)線程的run()方法中
class Father extends Thread{
public void run() {
Thread son=new Son();
son.start();
try {
son.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class Son extends Thread{
public void run() {
}
}
在上面的代碼中我們希望Son先執(zhí)行,我們可以在Father中實(shí)現(xiàn)son.join()來讓
son插隊(duì)到Father線程的前面執(zhí)行,那么對Father來說,就相當(dāng)于被插隊(duì),此時(shí)阻塞,當(dāng)插隊(duì)的son執(zhí)行完后Father并不立即執(zhí)行,而是回到就緒狀態(tài),等待cpu時(shí)間片
需要注意的是,操作系統(tǒng)控制的線程遇到類似I/O操作這類等待臨界資源導(dǎo)致的阻塞也是不釋放鎖的阻塞,在拿到臨界資源的鎖后就緒,隨時(shí)可以運(yùn)行,但這也使得多個(gè)線程占據(jù)不同的鎖,容易引起死鎖
(2)釋放鎖的阻塞
wait()和wait(long ,int)
wait()方法使線程失去時(shí)間片,失去鎖,并被冷藏,直到使用notify()/notifyAll()喚醒
notify()喚醒單個(gè)線程,notifyAll()喚醒所有
wait(long,int)和上面基本一樣,唯一不同的是這里可以設(shè)置一個(gè)參數(shù)為時(shí)間,在notify()喚醒前如果時(shí)間耗盡會自動蘇醒,不必等notify
另外,interrupt()也可以用在這里改變線程狀態(tài)
喚醒后的線程依然是阻塞狀態(tài),和喚醒前不同的是線程可以去爭奪資源鎖,此時(shí)就類似遇到同步鎖而被動阻塞線程,在獲取了所有鎖之后就進(jìn)入就緒狀態(tài),隨時(shí)可以運(yùn)行。