基本的用法就是定義計數器n,並通過await方法阻塞執行緒,直到n變成0。和join最直觀的使用區別就是不用手動的去掛起了,一兩個執行緒可能並不會顯得多方便,但是當執行緒數量較大時,區別還是很明顯的。
這個我也沒用過,其字面意思就是可迴圈使用的屏障,讓一組執行緒到達乙個屏障(同步點)時被阻塞,直到最後乙個執行緒到達屏障時,屏障才會開門,所有被屏障攔截的執行緒才會繼續執行。
semaphore翻譯過來就是訊號量,用於控制同時訪問特定資源的執行緒數量,通過acquire和release獲取訊號量(有點類似lock的底層實現),其構造方法也是乙個整形的數字,代表可用的許可證書,用於控制併發數量。
在看這本書前,這個我甚至都沒聽過,它是乙個用於執行緒間協作的工具類,用於進行執行緒間的資料交換,通過exchange方法執行執行緒之間的資料交換。
面試必問點,使用執行緒池有以下三個好處:
實現原理(書上原圖):
1)核心執行緒(corepoolsize)是否已滿,如果不是,則建立乙個新的工作執行緒來執行任務,如果核心執行緒池中的執行緒都在執行任務,則進入下乙個判斷;
2)判斷執行緒池的工作佇列(runnabletaskqueue)是否已滿,如果沒有,則將任務入佇列,如果滿了,則進入下乙個判斷;
3)執行緒池判斷執行緒池的執行緒是否都處於工作狀態,如果沒有,則建立乙個新的工作執行緒來執行任務,如果滿了,則交給飽和策略(rejectedexecutionhandler)來處理這個任務;
因此threadpoolexecutor的executor方法包括以下四種情況,用圖來表示如下:
這裡猜想下前文中提高響應速度的意思,在執行execute方法時,通過執行緒池的排程,盡可能的避免全域性鎖,因為當執行緒數量大於corepoolsize時,任務加入等待佇列,而這個步驟是不需要獲取鎖的,作者說的「執行」應該是指加入等待佇列吧,通過避免鎖而提高吞吐量,但實際上其執行緒中的業務並沒有真正開始執行。這裡貼一下核心的execute方法原始碼:
public void execute(runnable command)
//2if (isrunning(c) && workqueue.offer(command))
//5else if (!addworker(command, false))
//6reject(command);
}
1對應第一種情況,2對應第二種情況,3的作用是檢查執行緒池狀態,如果此時執行緒池不可用或非執行,則將這個加入的任務刪掉,並交由拒絕handler處理,其實仔細思考一下,這裡為什麼不將所有的任務都交由拒絕handler處理,因為執行緒池的狀態是可恢復的,此時要做的僅僅應該是拒絕新的任務,4發現沒有worker,則補充乙個worker,5對應第三種情況,6則對應第四種情況,這就是執行緒池的實現原理,執行緒的復用其實是worker的復用,執行緒池建立執行緒時,會將執行緒封裝成工作執行緒worker,這裡再貼一下worker的run原始碼:
public void run()
final void runworker(worker w) catch (runtimeexception x) catch (error x) catch (throwable x) finally
} finally
}completedabruptly = false; //5
} finally
}
1進入迴圈,從gettask獲取要執行的任務,直到返回null。這裡達到了執行緒復用的效果,讓執行緒處理多個任務。2保證了執行緒池在stop狀態下執行緒是中斷的,非stop狀態下執行緒沒有被中斷。3呼叫了run方法,任務的真正執行即在這一步。執行前後提供了beforeexecute和afterexecute兩個空方法,可以由子類實現。4中completedtasks統計worker執行了多少任務,最後累加進completedtaskcount變數,可以呼叫相應方法返回一些統計資訊。5的變數completedabruptly表示worker是否異常終止,執行到這裡代表執行正常,後續的方法需要這個變數,6呼叫processworkerexit結束。
這裡涉及到乙個關鍵的方法:gettask,這個方法負責從等待佇列中獲取任務,再貼一下原始碼:
private runnable gettask()
int wc = workercountof(c);
//2// are workers subject to culling?
boolean timed = allowcorethreadtimeout || wc > corepoolsize;
if ((wc > maximumpoolsize || (timed && timedout))
&& (wc > 1 || workqueue.isempty()))
//3try catch (interruptedexception retry) }}
可以看到,通過乙個無限的迴圈不停的執行以下三個步驟:
1、檢查執行緒池狀態,rs>=stop代表執行緒池為不可用的狀態,empty即等待隊列為空,這個判斷的意思就是如果執行緒池為shutdown狀態且等待佇列不為空,則繼續執行完等待佇列裡的任務,如果為不可用狀態(stop、tidying、terminated)則不再執行剩餘的任務。
2、這個判斷繞了我很久,大致的意思就是三個判斷:(1)空閒是否超時,超時則終止;(2)在執行過程中worker數量是否大於maxpoolsize(setmaxpoolsize方法導致最大執行緒數量在執行時變更);(3)等待隊列為空或worker數量大於等於1,即在等待佇列非空時,至少保留乙個worker。這三個判斷只要有乙個成立都不能處理等待佇列中的任務。
3、2不成立則調到該步驟,從等待佇列中取任務(take為阻塞式,前面已經介紹過)。
再來看看前面finally塊中的processworkerexit方法:
private void processworkerexit(worker w, boolean completedabruptly) finally
//3tryterminate();
int c = ctl.get();
//4if (runstatelessthan(c, stop))
addworker(null, false);}}
1判斷worker是否被中斷,如果是,則對workercount的減一,如果沒有的話,在gettask裡面已經對wc做了減一操作;2計算已處理的worker數量,並且在集合workers中去除;3嘗試終止執行緒池;4處理執行緒池還是running或shutdown狀態時,如果worker是異常結束,那麼會直接addworker。如果allowcorethreadtimeout=true,並且等待佇列有任務,至少保留乙個worker;如果allowcorethreadtimeout=false,workercount不少於corepoolsize。
執行緒池的關閉:可以通過shutdown或shutdownnow兩種方法來關閉執行緒池,它們的原理都是遍歷執行緒池中的工作執行緒,逐個呼叫interrupt方法,無法響應中斷的任務可能永遠無法終止,兩種方法區別如下:
執行緒池大小的配置:
實習的時候聽講座的人說過一句話印象特別深刻:大池小佇列,小池無佇列;即判斷執行緒池中的任務特性,如果是io密集型則用大池小佇列,讓它多去切換提高吞吐量,如果是cpu密集型,則使用小池無佇列,儘量減少執行緒的切換導致執行效率降低。cpu密集型任務執行緒池大小一般配置為:n+1,io密集型配置為2n,n為cpu核數,擴充套件下,netty中的eventloopgroup執行緒池的預設大小即為2n。
執行緒池的監控:
taskcount:執行緒池需要執行的任務數量;
completedtaskcount:執行緒池在執行過程中已完成的任務數量,小於或者等於taskcount;
largestpoolsize:執行緒池裡曾經建立過的最大執行緒數量。通過這個資料可以知道執行緒池是否曾經滿過;
getpoolsize:執行緒池的執行緒數量,如果執行緒池不銷毀,執行緒也不會銷毀,因此這個大小只會遞增;
getactivecount:獲取活動的執行緒數;
併發相關知識總結(三)
1 lock介面 關鍵點 1 lock和synchronized的區別,後者是通過monitor 物件頭的方式控制線程的同步,鎖的獲取和釋放都是隱式完成的,而lock則是手把手的獲取與釋放鎖 2 lock可以嘗試非阻塞的獲取鎖,以及超時獲取鎖,並且,與synchronized不同,獲取到鎖的執行緒能...
機器學習知識點相關總結(四) SVM相關
機器學習知識點相關總結 一 基礎 機器學習知識點相關總結 二 決策樹相關 機器學習知識點相關總結 三 lr相關 機器學習知識點相關總結 四 svm相關 機器學習知識點相關總結 五 cnn相關 機器學習知識點相關總結 六 rnn,lstm相關 機器學習知識點相關總結 七 k means相關 1.講一下...
併發程式設計的知識總結
對併發領程式設計從全面的去看待,可以抽象成三個核心問題 分工 同步 互斥 分工 分工重要且複雜 同步 乙個執行緒完成任務後,如何通知執行後續任務的執行緒 互斥 同一時刻,只允許乙個執行緒訪問共享變數 併發程式設計有三大問題 可見性 原子性 有序性 可見性產生的原因 計算機記憶體和cup的速度相差很多...