您好,登錄后才能下訂單哦!
小編給大家分享一下Thread線程中基礎知識及常見疑惑點有哪些,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
正文
1、Thread線程的狀態
根據《深入理解Java虛擬機》一書的講述,Java語言定義了五種線程狀態,分別為:創建(new)、運行(Runnable)、等待(waiting)、阻塞(blocked)、結束(terminated)。而且規定,在某一個時間點,每個線程能且只能處于其中的一種狀態。
其中,運行狀態又包括就緒(Ready)跟正在運行(Running),區別就是是否獲得了CPU的執行時間。
對于等待跟阻塞狀態,需要著重說明一下,因為此處極易搞錯,而且也是面試常被問到的點。等待狀態,一般由Object.wait()、Thread.sleep()、Thread.join()、LockSupport.park()等方法以及這些方法帶時間控制的同類方法實現線程的等待。而阻塞狀態,一般是由于當前線程還未獲取到獨占鎖且正在等待獲取,此時稱為阻塞。可以將等待看做主動的線程暫停執行,以為需要調用特定的方法線程才會等待;而阻塞可以看做是被動的線程暫定執行,因為線程在等著獲取獨占鎖。
2、Thread線程的相關方法
start()方法/run()方法:有時在面試的時候,面試官會問到調用線程的start方法跟直接調用run方法有什么區別?雖然有的道友看到這里會覺得問這種問題的面試官有點很沒必要,但我還是說一下。調用start方法后,最終會調用Thread類中的一個本地方法start0,這個方法可以新建一個線程來運行你的run方法,而調用run方法后只是在當前線程上運行你的run方法,并沒有新線程參與。
wait()方法/sleep()方法:請注意,這里很多人都會記錯,wait方法以及跟它配套的notify/notifyAll方法,是位于頂級父類Object下的,而其他操作線程的方法都在Thread線程類下。為什么要將wait方法放在Object下呢?其實這是由wait/notify方法的實現原理決定的。wait方法調用了之后,會釋放鎖,并讓當前線程等待,而對于java的原生鎖synchronized,是隸屬于一個特定對象的監視器monitor的,那這個釋放的是鎖誰的鎖?不能是別人的,只能是調用wait方法的那個對象的。而這個鎖是哪里來的?要釋放鎖,肯定之前加過鎖,在哪里加的呢?只能是在synchronized塊中給這個對象加的,所以這也解釋了為什么wait/notify方法一直要跟synchronized一起用,因為它倆就是通過操作對象的鎖實現的等待和喚醒。相比而言sleep方法單純很多,它只是讓當前線程睡眠一段時間,并不會涉及到對鎖的操作,所以直接放在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獲取到鎖,觸發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(); }
執行結果為:
thread獲取到鎖,觸發wait
thread1獲取到鎖
1秒后喚醒
notify over
wait over
LockSupport.park():另外還有JUC包中的park方法讓當前線程等待。此方法是使用CAS實現的線程等待,不會釋放鎖。而park/unpark方法比wait/notify這一對好的地方在于,前者可以先unpark在park,這是線程仍然會繼續執行;而對于wait/notify,則需要通過程序控制執行順序,一定要先wait在notify/notifyAll,否則順序反了線程就會一直等待下去,由此悲劇誕生... 比如講上述wait/notify的代碼34行35行調換一下順序,執行結果如下所示:
thread1獲取到鎖
1秒后喚醒
notify over
thread獲取到鎖,觸發wait
仿佛云天明對程心那一千八百萬年的等待
join()/yield():對于Thread下的這兩個方法,之所以放在一起講解,就是因為這兩個方法平時比較少用到,屬于閑云野鶴的存在。
yield()方法是讓當前線程讓步,讓步的意思就是放棄執行權,即當前線程會從上述說的運行狀態runnable中的running狀態進入ready就緒狀態,但是虛擬機不保證當前線程執行了yield方法后不會緊接著再次進去running狀態,因為可能CPU分配執行時間時又分給了當前線程。所以這個方法其實一般也沒啥用,因為效果不穩定。
join()方法是將調用join的線程插入當前線程的執行過程中,即讓當前線程等待,先執行完調用join的線程,再繼續執行當前線程。注意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"); } }
執行結果為:
runnable run
開始睡眠
睡了5秒
end
以上是“Thread線程中基礎知識及常見疑惑點有哪些”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。