JUC面試題筆記

2021-10-22 14:27:03 字數 4645 閱讀 2046

當多個執行緒共享同乙個全域性變數並對它進行寫(增刪改)操作的時候,可能會受到其他執行緒干擾,導致資料可能會產生執行緒安全問題。
執行緒池和資料庫執行緒池非常類似,可以統一管理和維護執行緒,減少沒必要的開銷。
因為頻繁的開啟執行緒或者停止執行緒,執行緒需要重新被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...