「Java多線程系列」線程間的互幫互助,一文讀懂通信與協作機制

本篇屬於【Java多線程系列】文章第二章【多線程編程的實現與應用】的第四小節內容,我們會來學習Java線程間的通信系統以及它們之前是如何進行協作的。讀懂本文需要掌握線程的生命週期知識,可以查看往期文章。

  • 線程間為什麼需要通信與協作

系統要實現某個全局功能,必定要需要各個子模塊之間的協調和配合。如同團隊要完成某項任務的時候,需要團隊各個成員之間密切配合一樣。

對於系統中的各個子線程來說,如果要完成一個系統功能,同樣需要各個線程的配合,這樣就少不了線程之間的通信與協作。

Java提供了幾種基本的線程通信與協作方式,接下來我們會對每一種進行介紹。

  • Thread.sleep()

讓當前線程睡眠一段時間,期間不會釋放任何持有的鎖,線程將會進入阻塞狀態(Blocked)。

<code>publicstaticnativevoidsleep(longmillis)throwsInterruptedException;/<code>

傳入的一個long類型的參數作為休眠時間,以毫秒為單位。注意當線程結束阻塞狀態後,不會直接繼續執行,而是會重新回到就緒狀態(Runnable)等待CPU資源。

因此假設我們傳入2000,讓線程休眠兩秒鐘,但是兩秒鐘後,線程不一定馬上重新繼續接下來的操作,而是會等待CPU資源的分配,在得到CPU執行時間片後才會繼續執行,因此從宏觀來看,整個暫停的過程可能會超過兩秒鐘。

  • Thread.yield()

使當前正在執行的線程,放棄當前分得的CPU時間片,直接重回就緒狀態(Runnable),不會進入阻塞狀態。

線程調度系統會對所有就緒狀態的線程進行重新選擇,然後分配CPU執行時間。所以執行yield()的線程有可能在進入到就緒狀態後馬上又被執行,也有可能直到重新分得CPU執行時間後才會重新執行。

<code>publicstaticnativevoidyield();/<code>
  • 為什麼sleep和yield都是Thread的靜態方法?

(1)Thread類的sleep()和yield()方法將在當前正在執行的線程上運行。所以在其他處於等待狀態的線程上調用這些方法是沒有意義的。這就是為什麼這些方法是靜態的。

(2)它們可以在當前正在執行的線程中工作,並避免程序員錯誤的認為可以在其他非運行線程調用這些方法。

  • join()

主線程啟動子線程,如果子線程中要進行大量的耗時運算,主線程會早於子線程結束。這時候可以調用子線程的join()方法,使子線程完成之後,主線再運行。

<code>publicfinalvoidjoin()throwsInterruptedException/<code>

【注意事項】join方法不會啟動調用線程,所以在調用join之前,該調用線程必須已經通過start啟動

【舉例】程序中一共有兩個線程t1和t2,為了充分利用多線程的效率,t1計算0~100000之和,t2計算100001~200000之和,然後主線程再對兩個和進行相加得到最終0~200000累加之和。

假如t1和t2的計算過程都很長,而主線程如果在t1和t2還沒有完成時就進行相加操作必然會造成錯誤。因此必須t1和t2已經執行完後,主線程再進行接下來的操作。

此時在t1和t2啟動後,調用他們的join方法,使主線程進行等待,之後他們已經運行結束後,主線程才會繼續運行。

這幾種基本的線程通信操作和協作方式是高階框架如fork-join的基礎,由於使用過程中極易出現錯誤,所以在他們基礎之上Java和很多開源框架已經封裝好了眾多功能,但是隻有對基礎原理有了一定認識,才能更好的應用框架技術。



分享到:


相關文章: