Thread線程的基礎(chǔ)知識及常見疑惑點(diǎn)總結(jié)
引言
相信各位道友在平時工作中已經(jīng)很少直接用到Thread線程類了,現(xiàn)在大多是通過線程池或者一些多線程框架來操作線程任務(wù),但我覺得還是有必要了解清楚Thread線程類中各種方法的含義,了解了底層才能更好的理解框架、應(yīng)用框架。下面我就將Thread線程的相關(guān)基礎(chǔ)點(diǎn)總結(jié)一二,以供觀瞻。
正文
1、Thread線程的狀態(tài)
根據(jù)《深入理解Java虛擬機(jī)》一書的講述,Java語言定義了五種線程狀態(tài),分別為:創(chuàng)建(new)、運(yùn)行(Runnable)、等待(waiting)、阻塞(blocked)、結(jié)束(terminated)。而且規(guī)定,在某一個時間點(diǎn),每個線程能且只能處于其中的一種狀態(tài)。
其中,運(yùn)行狀態(tài)又包括就緒(Ready)跟正在運(yùn)行(Running),區(qū)別就是是否獲得了CPU的執(zhí)行時間。
對于等待跟阻塞狀態(tài),需要著重說明一下,因?yàn)榇颂帢O易搞錯,而且也是面試常被問到的點(diǎn)。等待狀態(tài),一般由Object.wait()、Thread.sleep()、Thread.join()、LockSupport.park()等方法以及這些方法帶時間控制的同類方法實(shí)現(xiàn)線程的等待。而阻塞狀態(tài),一般是由于當(dāng)前線程還未獲取到獨(dú)占鎖且正在等待獲取,此時稱為阻塞??梢詫⒌却醋鲋鲃拥木€程暫停執(zhí)行,以為需要調(diào)用特定的方法線程才會等待;而阻塞可以看做是被動的線程暫定執(zhí)行,因?yàn)榫€程在等著獲取獨(dú)占鎖。
2、Thread線程的相關(guān)方法
start()方法/run()方法:有時在面試的時候,面試官會問到調(diào)用線程的start方法跟直接調(diào)用run方法有什么區(qū)別?雖然有的道友看到這里會覺得問這種問題的面試官有點(diǎn)很沒必要,但我還是說一下。調(diào)用start方法后,最終會調(diào)用Thread類中的一個本地方法start0,這個方法可以新建一個線程來運(yùn)行你的run方法,而調(diào)用run方法后只是在當(dāng)前線程上運(yùn)行你的run方法,并沒有新線程參與。
wait()方法/sleep()方法:請注意,這里很多人都會記錯,wait方法以及跟它配套的notify/notifyAll方法,是位于頂級父類Object下的,而其他操作線程的方法都在Thread線程類下。為什么要將wait方法放在Object下呢?其實(shí)這是由wait/notify方法的實(shí)現(xiàn)原理決定的。wait方法調(diào)用了之后,會釋放鎖,并讓當(dāng)前線程等待,而對于java的原生鎖synchronized,是隸屬于一個特定對象的監(jiān)視器monitor的,那這個釋放的是鎖誰的鎖?不能是別人的,只能是調(diào)用wait方法的那個對象的。而這個鎖是哪里來的?要釋放鎖,肯定之前加過鎖,在哪里加的呢?只能是在synchronized塊中給這個對象加的,所以這也解釋了為什么wait/notify方法一直要跟synchronized一起用,因?yàn)樗鼈z就是通過操作對象的鎖實(shí)現(xiàn)的等待和喚醒。相比而言sleep方法單純很多,它只是讓當(dāng)前線程睡眠一段時間,并不會涉及到對鎖的操作,所以直接放在Thread類中就行。對于wait跟notify的演示如下:
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
synchronized (obj) {
try {
System.out.println("thread獲取到鎖,觸發(fā)wait");
obj.wait();
System.out.println("wait over");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (obj) {
try {
System.out.println("thread1獲取到鎖");
Thread.sleep(1000);
System.out.println("1秒后喚醒");
obj.notify();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("notify over");
}
}
});
thread.start();
thread1.start();
}
執(zhí)行結(jié)果為:
thread獲取到鎖,觸發(fā)wait
thread1獲取到鎖
1秒后喚醒
notify over
wait over
LockSupport.park():另外還有JUC包中的park方法讓當(dāng)前線程等待。此方法是使用CAS實(shí)現(xiàn)的線程等待,不會釋放鎖。而park/unpark方法比wait/notify這一對好的地方在于,前者可以先unpark在park,這是線程仍然會繼續(xù)執(zhí)行;而對于wait/notify,則需要通過程序控制執(zhí)行順序,一定要先wait在notify/notifyAll,否則順序反了線程就會一直等待下去,由此悲劇誕生... 比如講上述wait/notify的代碼34行35行調(diào)換一下順序,執(zhí)行結(jié)果如下所示:
thread1獲取到鎖
1秒后喚醒
notify over
thread獲取到鎖,觸發(fā)wait
仿佛云天明對程心那一千八百萬年的等待
join()/yield():對于Thread下的這兩個方法,之所以放在一起講解,就是因?yàn)檫@兩個方法平時比較少用到,屬于閑云野鶴的存在。
yield()方法是讓當(dāng)前線程讓步,讓步的意思就是放棄執(zhí)行權(quán),即當(dāng)前線程會從上述說的運(yùn)行狀態(tài)runnable中的running狀態(tài)進(jìn)入ready就緒狀態(tài),但是虛擬機(jī)不保證當(dāng)前線程執(zhí)行了yield方法后不會緊接著再次進(jìn)去running狀態(tài),因?yàn)榭赡蹸PU分配執(zhí)行時間時又分給了當(dāng)前線程。所以這個方法其實(shí)一般也沒啥用,因?yàn)樾Ч环€(wěn)定。
join()方法是將調(diào)用join的線程插入當(dāng)前線程的執(zhí)行過程中,即讓當(dāng)前線程等待,先執(zhí)行完調(diào)用join的線程,再繼續(xù)執(zhí)行當(dāng)前線程。注意join方法不會釋放鎖。join的演示代碼如下:
public class RunnableThread implements Runnable{
@Override
public void run() {
System.out.println("runnable run");
try {
System.out.println("開始睡眠");
Thread.sleep(5000);
System.out.println("睡了5秒");
} catch (Exception e) {
System.out.println("runnable exception:" + e);
}
}
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
Thread thread = new Thread(new RunnableThread());
thread.start();
thread.join();
System.out.println("end");
}
}
執(zhí)行結(jié)果為:
runnable run
開始睡眠
睡了5秒
end
結(jié)束語
這次先到這里,上述說的東西,雖然很小,而且實(shí)際中不會直接用到,但是對于我們理解線程的運(yùn)行機(jī)制、理解多線程框架都有好處,所以還是有必要在自己的學(xué)習(xí)地圖上理解清楚。其實(shí)線程還有一個很重要的點(diǎn)就是線程的中斷,多線程框架或者JUC包的源碼中都會涉及到對線程中斷的處理以及響應(yīng),這一塊我會在后面梳理清楚了之后專門整理出來。最近覺得學(xué)習(xí)進(jìn)入了停滯期,有點(diǎn)不知道從何下手,覺得需要學(xué)的東西太多。在這里,想跟各位道友討教一下,一個資質(zhì)普通的開發(fā)者,如何才能將自己的實(shí)力提升到一個比較高的層次(比如阿里的P6P7及以上?)歡迎留言賜教,在此不勝感激!
相關(guān)文章
springboot+vue實(shí)現(xiàn)oss文件存儲的示例代碼
對象存儲服務(wù)是一種海量、安全、低成本、高可靠的云存儲服務(wù),本文主要介紹了springboot+vue實(shí)現(xiàn)oss文件存儲的示例代碼,具有一定的參考價值,感興趣的可以了解一下2024-02-02
Hibernate用ThreadLocal模式(線程局部變量模式)管理Session
今天小編就為大家分享一篇關(guān)于Hibernate用ThreadLocal模式(線程局部變量模式)管理Session,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-03-03
java獲取和設(shè)置系統(tǒng)變量問題(環(huán)境變量)
這篇文章主要介紹了java獲取和設(shè)置系統(tǒng)變量問題(環(huán)境變量),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-01-01

