Thread線程的基礎(chǔ)知識及常見疑惑點總結(jié)
引言
相信各位道友在平時工作中已經(jīng)很少直接用到Thread線程類了,現(xiàn)在大多是通過線程池或者一些多線程框架來操作線程任務(wù),但我覺得還是有必要了解清楚Thread線程類中各種方法的含義,了解了底層才能更好的理解框架、應(yīng)用框架。下面我就將Thread線程的相關(guān)基礎(chǔ)點總結(jié)一二,以供觀瞻。
正文
1、Thread線程的狀態(tài)
根據(jù)《深入理解Java虛擬機(jī)》一書的講述,Java語言定義了五種線程狀態(tài),分別為:創(chuàng)建(new)、運(yùn)行(Runnable)、等待(waiting)、阻塞(blocked)、結(jié)束(terminated)。而且規(guī)定,在某一個時間點,每個線程能且只能處于其中的一種狀態(tài)。
其中,運(yùn)行狀態(tài)又包括就緒(Ready)跟正在運(yùn)行(Running),區(qū)別就是是否獲得了CPU的執(zhí)行時間。
對于等待跟阻塞狀態(tài),需要著重說明一下,因為此處極易搞錯,而且也是面試常被問到的點。等待狀態(tài),一般由Object.wait()、Thread.sleep()、Thread.join()、LockSupport.park()等方法以及這些方法帶時間控制的同類方法實現(xiàn)線程的等待。而阻塞狀態(tài),一般是由于當(dāng)前線程還未獲取到獨占鎖且正在等待獲取,此時稱為阻塞??梢詫⒌却醋鲋鲃拥木€程暫停執(zhí)行,以為需要調(diào)用特定的方法線程才會等待;而阻塞可以看做是被動的線程暫定執(zhí)行,因為線程在等著獲取獨占鎖。
2、Thread線程的相關(guān)方法
start()方法/run()方法:有時在面試的時候,面試官會問到調(diào)用線程的start方法跟直接調(diào)用run方法有什么區(qū)別?雖然有的道友看到這里會覺得問這種問題的面試官有點很沒必要,但我還是說一下。調(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下呢?其實這是由wait/notify方法的實現(xiàn)原理決定的。wait方法調(diào)用了之后,會釋放鎖,并讓當(dāng)前線程等待,而對于java的原生鎖synchronized,是隸屬于一個特定對象的監(jiān)視器monitor的,那這個釋放的是鎖誰的鎖?不能是別人的,只能是調(diào)用wait方法的那個對象的。而這個鎖是哪里來的?要釋放鎖,肯定之前加過鎖,在哪里加的呢?只能是在synchronized塊中給這個對象加的,所以這也解釋了為什么wait/notify方法一直要跟synchronized一起用,因為它倆就是通過操作對象的鎖實現(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實現(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下的這兩個方法,之所以放在一起講解,就是因為這兩個方法平時比較少用到,屬于閑云野鶴的存在。
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),因為可能CPU分配執(zhí)行時間時又分給了當(dāng)前線程。所以這個方法其實一般也沒啥用,因為效果不穩(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é)束語
這次先到這里,上述說的東西,雖然很小,而且實際中不會直接用到,但是對于我們理解線程的運(yùn)行機(jī)制、理解多線程框架都有好處,所以還是有必要在自己的學(xué)習(xí)地圖上理解清楚。其實線程還有一個很重要的點就是線程的中斷,多線程框架或者JUC包的源碼中都會涉及到對線程中斷的處理以及響應(yīng),這一塊我會在后面梳理清楚了之后專門整理出來。最近覺得學(xué)習(xí)進(jìn)入了停滯期,有點不知道從何下手,覺得需要學(xué)的東西太多。在這里,想跟各位道友討教一下,一個資質(zhì)普通的開發(fā)者,如何才能將自己的實力提升到一個比較高的層次(比如阿里的P6P7及以上?)歡迎留言賜教,在此不勝感激!
相關(guān)文章
springmvc整合freemarker配置的詳細(xì)步驟
本篇文章主要介紹了springmvc整合freemarker的詳細(xì)步驟,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-05-05詳談spring中bean注入無效和new創(chuàng)建對象的區(qū)別
這篇文章主要介紹了spring中bean注入無效和new創(chuàng)建對象的區(qū)別,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02關(guān)于idea更新到2020.2.3無法創(chuàng)建web項目原因 library is not specified
這篇文章主要介紹了關(guān)于idea更新到2020.2.3無法創(chuàng)建web項目原因 library is not specified,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-10-10JavaWeb頁面中防止點擊Backspace網(wǎng)頁后退情況
當(dāng)鍵盤敲下后退鍵(Backspace)后怎么防止網(wǎng)頁后退情況呢?今天小編通過本文給大家詳細(xì)介紹下,感興趣的朋友一起看看吧2016-11-11