Java多執行緒的問題及答案
收集常見的問題,以後或許用得到
-
執行緒池的原理,為什麼要建立執行緒池?建立執行緒池的方式?
答:當向執行緒池提交一個任務的時候。
- 先看執行緒池中的核心執行緒是否有空閒的,如果有建立一個工作執行緒來執行任務。如果核心執行緒都在工作,那麼進入下一步
- 判斷任務佇列是否滿了,如果任務佇列未滿,則把任務儲存到任務佇列,執行下一步。如果滿了,執行拒絕策略。
- 新增到任務佇列之後,再判斷核心執行緒是否有空閒的,如果沒有空閒的,那麼嘗試建立新的非核心執行緒執行任務。
在實際的生產環境中,執行緒的數量必須得到控制,盲目的大量建立執行緒對系統性能是有傷害的,合理使用執行緒好處:
- 減少在建立和銷燬現場上所消耗的時間和系統資源
- 提高響應速度,無需建立可以直接執行
- 提高執行緒的可管理性。使用執行緒池可以進行統一分配,調優和監控,但是要做到合理利用執行緒池,必須對其原理了如指掌。
建立執行緒池的方式:
- Executors框架,有可能導致OOM異常
- 手動建立執行緒池,我們更明白執行緒池的引數,方便調優
參考:多執行緒之執行緒池(六)
-
執行緒的生命週期,什麼時候會出現僵死程序?
執行緒是輕量級的程序,程序可以說是執行緒的容器。
image
-
說說執行緒安全問題,什麼是實現執行緒安全,如何實現執行緒安全?
併發程式設計中最常出現的情形就是多個執行緒共享一個資源,這些共享的資源很可能導致錯誤或者資料不一致的情形,需要想辦法來解決這種問題。執行緒安全是多執行緒領域的問題,執行緒安全可以簡單理解為一個方法或者一個例項可以在多執行緒環境中使用而不會出現問題。
執行緒安全實現方式:
- 互斥同步,加鎖,悲觀方案,保證共享資料同一時刻只有一個執行緒訪問。,互斥是因,同步是果。
- 非阻塞同步,CAS,樂觀方案,先進行操作,如果沒有其他執行緒也進行操作,那麼就操作成功了,如果有其它執行緒也在操作共享資料,那麼再重試。
- 無同步方案,一般為純程式碼,有一些特性,如不依賴堆上的公用系統資源
參考:Java高效併發(九)
-
synchronized即可修飾非靜態方式,也可修飾靜態方法,還可修飾程式碼塊,有何區別?
答:synchronized修飾非靜態同步方法時,鎖住的是當前例項;synchronized修飾靜態同步方法時,鎖住的是該類的Class物件;synchronized修飾靜態程式碼塊時,鎖住的是synchronized關鍵字後面括號內的物件。
- 建立執行緒池有哪幾個核心引數? 如何合理配置執行緒池的大小?
- corePoolSize: 執行緒池的基本大小。當提交一個任務的時候,執行緒池就會建立一個新的執行緒執行任務,即使核心執行緒池中有空閒執行緒,也會新建,直到執行緒池中的數量等於corePoolSize就不再建立。如果呼叫了執行緒池的prestartAllCoreThreads()方法,執行緒池會提前建立並啟動所有的執行緒。
- maximumPoolSize:執行緒池允許建立的最大執行緒數。當使用無界佇列的時候,這個引數就沒什麼效果了。
- keepAliveTime:執行緒池的工作執行緒空閒以後,保持存活的時間,如果任務多,並且任務執行時間段,可以調大時間,提高執行緒的利用率。
- unit 保活時間的單位
- workQueue: 任務佇列,用於保持或等待執行的任務阻塞佇列。有如下佇列可供選擇:
- ArrayBlockingQueue: 基於陣列結構的有界佇列,此佇列按FIFO原則對元素進行排序
- LinkedBlockingQueue: 基於連結串列的阻塞佇列,FIFO原則,吞吐量通常高於ArrayBlockingQueue.
- SynchronousQueue: 不儲存元素的阻塞佇列。每個插入必須要等到另一個執行緒呼叫移除操作。
- PriorityBlockingQueue: 具有優先順序的無阻塞佇列
- threadFactory:用於設定建立執行緒的工廠。
- handler:拒絕策略,當佇列執行緒池都滿了,必須採用一種策略來處理還要提交的任務。在實際應用中,我們可以將資訊記錄到日誌,來分析系統的負載和任務丟失情況JDK中提供了4中策略:
- AbortPolicy: 直接丟擲異常
- CallerRunsPolicy: 只用呼叫者所在的執行緒來執行任務
- DiscardOldestPolicy: 丟棄佇列中最老的一個人任務,並執行當前任務。
- DiscardPolicy: 直接丟棄新進來的任務
執行緒池中執行緒的數量過大和過小都無法使系統的效能發揮到最優,確定執行緒池的大小可以考慮下面的角度:
- 任務性質:CPU密集,IO密集,和混合密集
- 任務執行時間:長,中,低
- 任務優先順序:高,中,低
- 任務的依賴性:是否依賴其它資源,如資料庫連線
- 建議使用有界佇列,防止撐爆記憶體
參考:多執行緒之執行緒池(六)
-
volatile、ThreadLocal的使用場景和原理?
JMM中主要是圍繞併發過程中如何處理原子性,可見性和有序性三個特性來建立的。最終可以保證執行緒安全性.
一個變數定義為volatile之後,它將具有兩種特性:
- 保證次變了對所有執行緒的可見性,一條執行緒修改了這個值,新值對其它執行緒是可以立即得知的。
- 禁止指令重排優化。
Synchronized保證了原子性,可見性與有序性,它的工作時對同步的程式碼塊加鎖,使得每次只有一個執行緒進入程式碼塊,從而保證執行緒安全。synchronized反應到位元組碼層面就是monitorenter與monitorexit.
Volatile適合做什麼?
適合做標量,當一個執行緒對某個變數進行讀寫操作,而其它執行緒僅僅進行讀操作的時候,是可以保證volatile的正確性的。如下:
volatile bool stopped; public void stop(){ stopped = true } while(!stoppped){ // 執行操作 }
參考: 多執行緒之volatile與synchronized(二)
-
ThreadLocal什麼時候會出現OOM的情況?為什麼?
答:當一個執行緒呼叫ThreadLocal的set方法設定變數時候,當前執行緒的ThreadLocalMap裡面就會存放一個記錄,這個記錄的key為ThreadLocal的引用,value則為設定的值。如果當前執行緒一直存在而沒有呼叫ThreadLocal的remove方法,並且這時候其它地方還是有對ThreadLocal的引用,則當前執行緒的ThreadLocalMap變數裡面會存在ThreadLocal變數的引用和value物件的引用是不會被釋放的,這就會造成記憶體洩露的。
最後
持續更新,多執行緒相關問題