當多個執行緒共享同乙個全域性變數並對它進行寫(增刪改)操作的時候,可能會受到其他執行緒干擾,導致資料可能會產生執行緒安全問題。
執行緒池和資料庫執行緒池非常類似,可以統一管理和維護執行緒,減少沒必要的開銷。
因為頻繁的開啟執行緒或者停止執行緒,執行緒需要重新被cpu從就緒到執行狀態之間的排程,需要發生cpu的上下文切換,效率非常低。
核心點:復用機制(提前建立好固定的執行緒一直在執行狀態,實現復用:限制執行緒建立數量)
1、降低資源消耗:通過池化技術重複利用已建立的執行緒,降低執行緒建立和銷毀造成的損耗。
2、提高響應速度:任務到達時,無需等待執行緒建立即可立即執行。
3、提高執行緒的可管理性:執行緒是稀缺資源,如果無限制建立,不僅會消耗系統資源,還會因為執行緒的不合理分布導致資源排程失衡,降低系統的穩定性。使用執行緒池可以進行統一的分配、調優和監控。
4、提供更多更強大的功能:執行緒池具備可拓展性,允許開發人員向其中增加更多的功能。比如延時定時執行緒池 scheduledthreadpoolexecutor,就允許任務延期執行或定期執行。
executors.
newcachedthreadpool()
;// 可快取執行緒池
executors.
newfixedthreadpool()
;// 可定長度 限制最大執行緒數
executors.
newscheduledthreadpool()
;// 可定時
executors.
newsinglethreadexecutor()
;// 單例
// 底層都是基於 threadpoolexecutor 建構函式封裝
本質思想:建立乙個執行緒,不會立馬停止或者銷毀,而是一直實現復用。
1、提前建立固定大小的執行緒一直保持在(正在執行)狀態;(可能會非常消耗cpu的資源)
2、當需要執行緒執行任務,將該任務提交快取在併發佇列中;如果佇列滿了,則會執行拒絕策略;
3、正在執行的執行緒從併發佇列中獲取任務執行,從而實現多執行緒復用問題;
引數說明
corepoolsize核心執行緒數量 一直正在保持執行的執行緒
maximumpoolsize最大執行緒數,執行緒池允許建立的最大執行緒數
keepalivetime超出 corepoolsize 後建立的執行緒的存活時間
unitkeepalivetime 的時間單位
workqueue任務佇列,用於儲存待執行的任務
threadfactory執行緒池內部建立執行緒所用的工廠
handler
任務無法執行時的處理器
/**
* 【思路】
* 1、提前建立好固定的執行緒一直才執行狀態(死迴圈實現)
* 2、提交的執行緒任務快取到乙個併發佇列集合中,交給我們正在執行的執行緒執行
* 3、正在執行的執行緒就從佇列中獲取該任務執行
*/public
class
myexecutors
}public
void
execute
(runnable runnable)
class
workthread
extends
thread}}
}public
static
void
main
(string[
] args)})
;}myexecutors.isrun =
false;}
}/**
* 執行結果
* * thread-0 -- 0
* thread-1 -- 1
* thread-0 -- 2
* thread-1 -- 3
* thread-0 -- 4
* thread-1 -- 5
* thread-0 -- 6
* thread-1 -- 7
* thread-0 -- 8
* thread-1 -- 9
* */
不會
例如:配置核心執行緒數 corepoolsize 為 2 、最大執行緒數 maximumpoolsize 為 5
我們可以通過配置超出 corepoolsize 核心執行緒數後建立的執行緒的存活時間例如為 60s
在 60s 內沒有核心執行緒一直沒有任務執行,則會停止該執行緒。
因為預設的 executors 執行緒池底層是基於 threadpoolexecutor建構函式封裝的,
採用無界佇列存放快取任務,會無限快取任務容易發生記憶體溢位,會導致我們最大執行緒數會失效。
如果佇列滿了,且(任務總數 > 最大執行緒數)則當前執行緒走拒接策略。
可以自定義拒絕異常,將該任務快取到redis、本地檔案、mysql中後期專案啟動實現補償。
abortpolicy)丟棄任務
callerrunspolicy)執行任務
discardpolicy)忽視,什麼都不會發生
discardoldestpolicy)從佇列中踢出最先進入佇列(最後乙個執行)的任務
實現rejectedexecutionhandler介面,可自定義處理器
自定義執行緒池就需要我們自己配置最大執行緒數 maximumpoolsize,為了高效的併發執行,當
然這個不能隨便設定。這時需要看我們的業務是 io 密集型還是 cpu 密集型。
// cpu 密集型
cpu 密集的意思是該任務需要大量的運算,而沒有阻塞,cpu 一直全速執行。cpu 密集任
務只有在真正的多核 cpu 上才可能得到加速(通過多執行緒),而在單核 cpu 上,無論你開幾
個模擬的多執行緒該任務都不可能得到加速,因為 cpu 總的運算能力就那些。
cpu 密集型任務配置盡可能少的執行緒數量:以保證每個 cpu 高效的執行乙個執行緒。
一般公式:(cpu 核數+
1)個 執行緒的執行緒池
// io 密集型
i0 密集型,即該任務需要大量的 io,即大量的阻塞。在單執行緒上執行 i0 密集型的任務會導
致浪費大量的 cpu 運算能力浪費在等待。
所以在 io 密集型任務中使用多執行緒可以大大的加速程式執行,即使在單核 cpu 上,這種加
速主要就是利用了被浪費掉的阻塞時間。
i0 密集型時,大部分執行緒都阻寒,故需要多配置執行緒數:
公式:cpu 核數 *
2cpu 核數 / (1
- 阻塞係數) 阻塞係數 在 0.8
~0.9 之間
悲觀鎖
比較悲觀、做寫的操作的時候會上鎖,保證只有乙個執行緒對該資料做操作,當沒有獲取到鎖的情況下,則當前執行緒
會變成阻塞狀態,想要繼續獲取到鎖必須經過就緒狀態,重新被cpu排程,效率非常低。
樂觀鎖
比較樂觀、也就是沒有鎖的概念,做寫操作的時候也就是不會上鎖,採用閾值或者版本號比較的形式實現資料更新,
如果更新不成功的情況下,則版本號可能發生了變化,則一直不斷迴圈更新,當前執行緒是不會阻塞的,一直為執行
狀態,效率比較高,但是樂觀鎖比較消耗cpu的資源。
樂觀鎖屬於無所機制,沒有鎖競爭流程。
在我們表結構中,會新增乙個字段就是版本字段 `version` varchar
(255
) default null,
多個執行緒對同一行資料實現修改操作,提前查詢當前最新的 version 版本號碼,
作為 update 條件查詢,如果當前 version 版本號碼發生了變化,則查詢不到該資料。
表示如果修改資料失敗,則不斷重試,又重新查詢最新的版本實現 update。
需要注意控制樂觀鎖迴圈的次數,避免 cpu 飆高的問題。
悲觀鎖與樂觀鎖
公平鎖與非公平鎖
自旋鎖/重入鎖
重量級鎖與輕量級鎖
獨佔鎖與共享鎖
公平鎖:就是比較公平,根據請求所的順序排序,先來請求的就先獲取鎖,後來獲取鎖就最後獲取到;採用佇列存放,類似吃飯排隊。
非公平鎖不是根據請求的順序排列,通過爭搶的方式獲取鎖。
非公平鎖效率比公平鎖效率要高,synchronized 是非公平鎖。
new reentramtlock()(true)公平鎖
new reentramtlock()(false)非公平鎖
底層基於 aqs 實現
nodejs面試題筆記
一 nodejs是什麼 二 nodejs和前端js的區別 1 語法層面 2 應用層面 三 nodejs如何除錯 四 當前檔案和當前目錄的路徑,如何獲取 五 commonjs 和 es6 module 的區別const http 六 path.resolve 和 path.join 的區別const ...
Vue面試題筆記
面試題1 路由傳遞引數 物件寫法 path是否可以結合params引數一起使用?答 路由跳轉傳參的時候,物件的寫法可以是name,path形式,但是需要注意的是,path這種寫法與params引數一起使用 this.router.push query 錯誤 路由傳遞引數的三種方式 第一種 字串形式 ...
面試題 PHP面試題
建議 比如是系統配置,缺少了無法執行,自然使用 require 如果某一段程式少了,只是少了統計 訪問的,不是必不可少的。可以使用 include 而加不加 once 就是效率上的區別,雖然系統會幫你考慮只包含一次,但系統的判斷會降低效率,因此,更應該在開發之初,把目錄結構調整高好,盡量不使用 on...