前言
線程間通訊主要是經過wait、notify來實現的,使用這種機制實現線程通訊是很是效率的,相比而言,不知道的同窗針對線程通訊可能只會想到輪詢的方式,下次可別再去輪詢共享變量了,把線程協做機制用起來。編程
1、線程協做的要素
首先,若是瞭解顯式鎖ReentrantLock或顯式條件Condition,咱們就會知道鎖的隊列不止有一個等待隊列,還有一個等待條件隊列,存放等待被喚醒的線程。 對於用聲明式編程synchronized關鍵字來講,底層也是這種原理,對應的針對等待條件隊列入隊出隊方法就是鎖資源的wait/notify/notifyAll方法。但光有這種機制只能表示咱們線程能夠觸發其餘線程繼續執行,前面說了叫等待條件隊列,那條件究竟是什麼勒?通常來講條件就是線程間共享的一個變量,這個變量用於控制線程等待或繼續執行。總結來講,notify通常伴隨着一個條件共享變量的改變,wait通常伴隨着一個條件共享變量的不知足。好比以下代碼:併發
synchronized (this) {
while (!condition) {
wait();
}
}
最開始condition不知足,該線程放棄CPU執行權,進入等待條件隊列,而後等到其餘線程作了其餘過後,條件共享變量被改變,而後該線程被喚醒,而後繼續執行。異步
2、線程協做的場景
線程間的基本協做機制大體分爲如下幾種:this
- 生產者/消費者協做模式,生產者線程和消費者線程共享一個隊列變量,爲了控制隊列的長度上限,當隊列爲滿時,限制生產者線程等待,當添加信息到隊列時,隊列不爲空,喚醒消費者隊列,當隊列爲空時限制消費者線程等待,取到信息時,隊列不爲滿,喚醒生產者隊列。
- 同時開始,全部子線程根據一個共享變量等待一個條件,同時開始的意思是主線程改變這個共享變量同時移除全部在等待條件隊列中的線程,如模擬仿真程序中,要求多個線程能同時開始。
- 等待結束,thread.join()方法的底層原理是while(子線程未結束){wait(0)},等待子線程結束。更好的方式實現是經過Java的CountDownLatch類來控制主線程的等待,子線程每結束一個線程計數器減1,主線程的等待條件是計數器爲0,使用線程計數器也能夠實現同時開始,只須要讓子線程共享線程計數器變量,等待條件是線程計數器爲0,主線程將一個線程計數器傳入多個子線程並運行子線程,在一個時刻將線程計數器countDown爲0就好了。這個主要是應用在主從協做模式中,主線程將任務分解爲若干個子任務,爲每一個子任務建立一個線程,主線程在繼續執行其餘任務以前須要等待每一個子任務執行完畢。
- 異步結果,jdk併發包對異步任務進行了封裝,主要用於主從協做場景,若是不想手工建立線程,管理線程可使用Excutors異步任務執行服務,核心是Future接口的get方法,該方法將等待異步任務返回執行結果,而後纔會被喚醒並返回結果。
- 集合點,併發包中的CyclicBarrier柵欄就是這種場景的應用,一組子線程同時等待全部其餘線程到一個地方以後再開始執行下面的邏輯,如一些並行計算場景,每一個線程負責一部分計算,而後在集合點等待其餘線程完成,全部線程到齊後,交換數據和計算結果,再進行下一次計算。