java 变量锁_并发编程高频面试题:可重入锁+线程池+内存模型等(含答案)
對(duì)于一個(gè)Java程序員而言,能否熟練掌握并發(fā)編程是判斷他優(yōu)秀與否的重要標(biāo)準(zhǔn)之一。因?yàn)椴l(fā)編程是Java語(yǔ)言中最為晦澀的知識(shí)點(diǎn),它涉及操作系統(tǒng)、內(nèi)存、CPU、編程語(yǔ)言等多方面的基礎(chǔ)能力,更為考驗(yàn)一個(gè)程序員的內(nèi)功。
那到底應(yīng)該怎么學(xué)習(xí)并發(fā)編程呢? Java SDK的并發(fā)工具包有很多,是要死記硬背每-一個(gè)工 具的優(yōu)缺點(diǎn)和使用場(chǎng)景嗎?當(dāng)然不是,想要學(xué)好并發(fā)編程,你需要從一個(gè)個(gè)單一的知識(shí)和技術(shù)中“跳出來”,高屋建瓴地看問題,并逐步建立自己的知識(shí)體系。
可 重 入 鎖 ReentrantLock 及 其 他 顯 式 鎖 相 關(guān) 問 題問題一: 跟Synchronized相比, 可重入鎖Reentrant Lock其實(shí)現(xiàn)原理有什么不同?
其實(shí), 鎖的實(shí)現(xiàn)原理基本是為了達(dá)到一個(gè)目的: 讓所有的線程都能看到某種標(biāo)記。Synchronized 通 過 在 對(duì) 象頭 中 設(shè) 置 標(biāo) 記 實(shí) 現(xiàn) 了這 一 目 的 , 是 一 種 JVM 原生的鎖實(shí)現(xiàn)方式, 而 Reentrant Lock 以及所有的基于 Lock 接口的實(shí)現(xiàn)類, 都是通 過用一個(gè) volitile 修飾的 int 型變量, 并保證每個(gè)線程 都 能擁 有 對(duì) 該 int 的可見性和原子修改, 其本質(zhì)是基于所謂的 AQS 框架。問題二: 那么請(qǐng)談?wù)?AQS 框架是怎么回事兒?
AQS( Abstract Queued Synchronizer 類 ) 是 一 個(gè) 用 來 構(gòu) 建 鎖 和 同 步 器的框架, 各種Lock包中的鎖( 常用的有Reentrant Lock、ReadWrite Lock) , 以及其他如 Semaphore、 Count Down Latch, 甚至 是 早期的 Future Task 等, 都是基于 AQS 來 構(gòu)建 。
1. AQS 在 內(nèi) 部 定 義 了 一 個(gè) volatile int state 變 量 , 表 示 同 步 狀 態(tài) : 當(dāng) 線程調(diào)用 lock 方法時(shí) , 如果 state= 0 , 說明沒有任何線程占有共享資源的鎖, 可以獲得鎖并將 state= 1 ; 如果 state= 1 , 則說明有線程目前正在使用共享變量, 其他線程必須加入同步隊(duì)列進(jìn)行等待。
2. AQS 通 過 Node 內(nèi) 部 類 構(gòu) 成 的 一 個(gè) 雙 向 鏈 表 結(jié) 構(gòu) 的 同 步 隊(duì) 列,來完成線程獲取鎖的排隊(duì)工作, 當(dāng)有線程獲取鎖失敗后, 就被添加到隊(duì)列末尾。
- Node 類 是 對(duì) 要 訪 問 同 步 代 碼 的 線 程 的 封 裝 , 包 含 了 線 程 本 身 及 其 狀 態(tài) 叫wait Status( 有五種不同 取值, 分別表示是否被阻塞, 是否等待喚醒, 是否 已 經(jīng) 被 取 消 等 ) , 每個(gè) Node 結(jié)點(diǎn)關(guān)聯(lián)其prev 結(jié)點(diǎn)和next 結(jié) 點(diǎn),方便線程釋放鎖后快速喚醒下一個(gè)在等待的線程, 是一個(gè) FIFO 的過程。
- Node 類 有 兩 個(gè) 常 量 , SHARED 和 EXCLUSIVE, 分 別 代 表 共 享 模 式 和 獨(dú)占 模 式 。 所 謂 共 享 模 式 是 一 個(gè) 鎖 允 許 多 條 線 程 同 時(shí) 操 作 ( 信 號(hào) 量Semaphore 就 是 基 于 AQS的 共 享 模 式 實(shí) 現(xiàn) 的 ) , 獨(dú) 占 模 式 是 同 一 個(gè) 時(shí)間 段 只 能 有 一 個(gè) 線 程 對(duì) 共 享 資 源 進(jìn) 行 操 作 , 多 余 的 請(qǐng) 求 線 程 需 要 排 隊(duì) 等 待( 如 Reentran Lock) 。
3. AQS
通 過 內(nèi) 部 類Condition Object構(gòu) 建 等 待 隊(duì) 列 ( 可 有 多 個(gè) ) , 當(dāng)Condition調(diào) 用wait()方 法 后 , 線 程 將 會(huì) 加 入 等 待 隊(duì) 列 中 , 而 當(dāng)Condition 調(diào) 用 signal() 方 法 后 , 線 程 將 從 等 待 隊(duì) 列 轉(zhuǎn) 移 動(dòng) 同 步 隊(duì) 列 中進(jìn) 行 鎖 競(jìng) 爭(zhēng) 。
4. AQS
和Condition各 自 維 護(hù) 了 不 同 的 隊(duì) 列 , 在 使 用Lock和Condition 的時(shí)候, 其實(shí)就是兩個(gè)隊(duì)列的互相移動(dòng)。問題三: 請(qǐng)盡可能詳盡地對(duì)比下Synchronized和Reentrant Lock的異同。
Reentrant Lock 是 Lock 的實(shí)現(xiàn)類, 是一個(gè)互斥的同步鎖。
從 功 能 角 度 , Reentrant Lock比Synchronized的 同 步 操 作 更 精 細(xì)( 因 為 可 以 像 普 通 對(duì) 象 一 樣 使 用 ) , 甚 至 實(shí) 現(xiàn)Synchronized沒 有 的高 級(jí) 功 能 , 如 :
- 等待可中斷: 當(dāng)持有鎖的線程長(zhǎng)期不釋放鎖的時(shí)候, 正在等待的線程可以選擇放 棄等待, 對(duì)處理 執(zhí)行時(shí)間 非常長(zhǎng)的 同步塊很 有用。
- 帶超時(shí)的獲取鎖嘗試: 在指定的時(shí)間范圍內(nèi)獲取鎖, 如果時(shí)間到了仍然無法獲取則返回 。
- 可 以 判 斷 是 否 有 線 程 在 排 隊(duì) 等 待 獲 取 鎖 。
- 可 以 響 應(yīng) 中 斷 請(qǐng) 求 : 與Synchronized不 同 , 當(dāng) 獲 取 到 鎖 的 線 程 被 中斷 時(shí) , 能 夠 響 應(yīng) 中 斷 , 中 斷 異 常 將 會(huì) 被 拋 出 , 同 時(shí) 鎖 會(huì) 被 釋 放 。
- 可 以 實(shí) 現(xiàn) 公 平 鎖 。
從鎖釋放角度, Synchronized 在 JVM 層面上實(shí)現(xiàn)的, 不但可以通過一些監(jiān)控工具監(jiān)控 Synchronized 的鎖定, 而且在代碼執(zhí)行出現(xiàn)異常時(shí), JVM 會(huì)自動(dòng)釋放鎖定;但是使用 Lock 則不行, Lock 是通過代碼實(shí)現(xiàn)的, 要保證鎖定一定會(huì)被釋放, 就必須將 un Lock() 放到f inally{} 中。
從 性 能 角 度 , Synchronized早 期 實(shí) 現(xiàn) 比 較 低 效 , 對(duì) 比Reentrant Lock, 大多數(shù)場(chǎng)景性能都相差較大。但 是 在 Java 6 中 對(duì) 其 進(jìn) 行 了 非 常 多 的 改 進(jìn) , 在競(jìng)爭(zhēng)不激烈時(shí) ,
Synchronized 的 性 能 要 優(yōu) 于 Reetrant Lock ; 在 高 競(jìng) 爭(zhēng) 情 況 下 ,Synchronized 的性 能會(huì)下降 幾十倍, 但是 Reetrant Lock 的 性 能 能 維 持常態(tài)。問題四: Reentrant Lock 是如何實(shí)現(xiàn)可重入性的?
Reentrant Lock 內(nèi) 部 自 定 義 了 同 步 器 Sync( Sync 既實(shí)現(xiàn)了 AQS, 又實(shí)現(xiàn)了 AOS, 而 AOS提 供 了 一 種 互 斥 鎖 持 有 的 方 式 ) , 其實(shí)就是加鎖的 時(shí) 候 通 過 CAS 算法, 將線程對(duì)象放到一個(gè)雙向鏈表中, 每次獲取 鎖 的時(shí) 候 , 看 下 當(dāng) 前 維 護(hù) 的 那 個(gè) 線 程 ID 和 當(dāng) 前 請(qǐng) 求 的 線 程 ID 是 否一 樣 ,一樣就可重入了。
問題五: 除了 Reetrant Lock, 你還接觸過 JUC 中的哪些并發(fā)工具?
問題六: 請(qǐng)談?wù)?Read Write Lock 和 Stamped Lock。
問題七: 如何讓 Java 的線程彼此同步? 你了解過哪些同步器? 請(qǐng)分別介紹下。
問題八: Cyclic Barrier和Count Down Latch 看起來很相似, 請(qǐng)對(duì)比下呢?
Java 線程池相關(guān)問題問題一: Java 中的線程池是如何實(shí)現(xiàn)的?
? 在 Java 中 , 所 謂 的 線 程 池 中 的 “ 線 程 ” , 其 實(shí) 是 被 抽 象 為 了 一 個(gè) 靜 態(tài)內(nèi) 部 類Worker, 它 基 于AQS實(shí) 現(xiàn) , 存 放 在 線 程 池 的Hash Set< Worker> workers 成 員 變 量 中 ;
? 而 需 要 執(zhí) 行 的 任 務(wù) 則 存 放 在 成 員 變 量work Queue( Blocking Queue< Runnable> work Queue) 中。這 樣 , 整 個(gè) 線 程 池 實(shí) 現(xiàn) 的 基 本 思 想 就 是 : 從work Queue中 不 斷 取 出需 要 執(zhí) 行 的 任 務(wù) , 放 在 Workers 中 進(jìn) 行 處 理 。問題二: 創(chuàng)建線程池的幾個(gè)核心構(gòu)造參數(shù)?
Java中 的 線 程 池 的 創(chuàng) 建 其 實(shí) 非 常 靈 活 , 我 們 可 以 通 過 配 置 不 同 的 參數(shù), 創(chuàng)建出行為不同的線程池, 這幾個(gè)參數(shù)包括:? core Pool Size: 線程池的核心線程數(shù)。
- maximum Pool Size: 線程池允許的最大線程數(shù)。
- keep Alive Time: 超過核 心線程數(shù) 時(shí)閑置線 程的存活 時(shí)間。
- work Queue: 任 務(wù) 執(zhí) 行 前 保 存 任 務(wù) 的 隊(duì) 列 , 保 存 由 execute 方 法 提 交的 Runnable 任 務(wù) 。
問題三: 線程池中的線程是怎么創(chuàng)建的? 是一開始就隨著線程池的啟動(dòng)創(chuàng)建好的嗎?
顯 然 不 是 的 。 線 程 池 默 認(rèn) 初 始 化 后 不 啟 動(dòng)Worker, 等 待 有 請(qǐng) 求 時(shí) 才 啟動(dòng) 。每 當(dāng) 我 們 調(diào) 用execute()方 法 添 加 一 個(gè) 任 務(wù) 時(shí) , 線 程 池 會(huì) 做 如 下 判斷 :
? 如 果 正 在 運(yùn) 行 的 線 程 數(shù) 量 小 于core Pool Size, 那 么 馬 上 創(chuàng) 建 線 程 運(yùn) 行這 個(gè) 任 務(wù) ;
? 如 果 正 在 運(yùn) 行 的 線 程 數(shù) 量 大 于 或 等 于core Pool Size, 那 么 將 這 個(gè) 任 務(wù)放 入 隊(duì) 列 ;
? 如果這 時(shí)候隊(duì)列 滿了, 而且正 在運(yùn)行的 線程數(shù)量 小于maximum Pool Size, 那么還 是要?jiǎng)?chuàng)建 非核心線 程立刻運(yùn) 行這個(gè)任 務(wù);
? 如果隊(duì) 列滿了, 而且正 在運(yùn)行的 線程數(shù)量 大于或等 于maximum Pool Size, 那么線程池會(huì)拋出異常Reject Execution Exception。當(dāng) 一 個(gè) 線 程 完 成 任 務(wù) 時(shí) , 它 會(huì) 從 隊(duì) 列 中 取 下 一 個(gè) 任 務(wù) 來 執(zhí) 行 。當(dāng) 一 個(gè)線 程 無 事 可 做 , 超 過 一 定 的 時(shí) 間 ( keep Alive Time) 時(shí) , 線 程 池 會(huì)斷。
如 果 當(dāng) 前 運(yùn) 行 的 線 程 數(shù) 大 于core Pool Size, 那么 這個(gè)線 程就 被停掉 。所以線程池的所有任務(wù)完成后, 它最終會(huì)收縮到core Pool Size的大小。
問題四: 既然提到可以通過配置不同參數(shù)創(chuàng)建出不同的線程池, 那么Java 中默認(rèn)實(shí)現(xiàn)好的線程池又有哪些呢? 請(qǐng)比較它們的異同。
問題五: 如何在 Java 線程池中提交線程?
Java 內(nèi)存模型相關(guān)問題問題一: 什么是 Java 的內(nèi)存模型, Java中各個(gè)線程是怎么彼此看到對(duì)方的變量的?
Java 的內(nèi)存模型定義了程序中各個(gè)變量的訪問規(guī)則, 即在虛擬機(jī)中將變量存儲(chǔ)到內(nèi)存和從內(nèi)存中取出這樣的底層細(xì)節(jié)。
此處的變量包括實(shí)例字段、 靜態(tài)字段和構(gòu)成數(shù)組對(duì)象的元素, 但是不包括局部變量和方法參數(shù), 因?yàn)檫@些是線程私有的, 不會(huì)被共享, 所以不存在競(jìng)爭(zhēng)問題。
Java 中各個(gè)線程是怎么彼此看到對(duì)方的變量的呢? Java 中定義了主內(nèi)存與工作內(nèi)存的概念:
所有的變量都存儲(chǔ)在主內(nèi)存, 每條線程還有自己的工作內(nèi)存, 保存了被該線程使用到的變量的主內(nèi)存副本拷貝。線程對(duì)變量的所有操作( 讀取、 賦值) 都必須在工作內(nèi)存中進(jìn)行, 不能直接讀寫主內(nèi)存的變量。 不同的線程之間也無法直接訪問對(duì)方工作內(nèi)存的變量, 線程間變量值的傳遞需要通過主存。
問題二: 請(qǐng)談?wù)?volatile 有什么特點(diǎn), 為什么它能保證變量對(duì)所有線程的可見性?
問題三: 既然 volatile 能夠保證線程間的變量可見性, 是不是就意味著基于volatile 變量的運(yùn)算就是并發(fā)安全的?
問題四: 請(qǐng)對(duì)比下 volatile 對(duì)比 Synchronized 的異同。
問題五: 請(qǐng)談?wù)?Thread Local 是怎么解決并發(fā)安全的?
問題六: 很多人都說要慎用 Thread Local, 談?wù)勀愕睦斫?#xff0c; 使用Thread Local 需要注意些什么?
Synchronized 相關(guān)問題
問題一: Synchronized 用過嗎, 其原理是什么?
問題二: 你剛才提到獲取對(duì)象的鎖, 這個(gè)“ 鎖” 到底是什么? 如何確定對(duì)象的鎖?
問題三: 什么是可重入性, 為什么說 Synchronized 是可重入鎖? 可 重 入 性
問題四: JVM 對(duì) Java 的原生鎖做了哪些優(yōu)化?
問題五: 為什么說 Synchronized 是非公平鎖?
問題六: 什么是鎖消除和鎖粗化?
問題七: 為什么說Synchronized是一個(gè)悲觀鎖? 樂觀鎖的實(shí)現(xiàn)原理又是什么? 什么是CAS, 它有什么特性?
問題八: 樂觀鎖一定就是好的嗎?
其他問題
JAVA 并發(fā)知識(shí)庫(kù)
JAVA 線程實(shí)現(xiàn)/創(chuàng)建方式
4 種線程池
線程生命周期(狀態(tài))
終止線程 4 種方式
sleep 與 wait 區(qū)別
start 與 run 區(qū)別
JAVA 后臺(tái)線程
JAVA 鎖
線程基本方法
線程上下文切換
同步鎖與死鎖
線程池原理
JAVA 阻塞隊(duì)列原理
CyclicBarrier、CountDownLatch、Semaphore 的用法
volatile 關(guān)鍵字的作用(變量可見性、禁止重排序)
如何在兩個(gè)線程之間共享數(shù)據(jù)
ThreadLocal 作用(線程本地存儲(chǔ))
synchronized 和 ReentrantLock 的區(qū)別
ConcurrentHashMap 并發(fā)
Java 中用到的線程調(diào)度
進(jìn)程調(diào)度算法
什么是 CAS(比較并交換-樂觀鎖機(jī)制-鎖自旋)
什么是 AQS(抽象的隊(duì)列同步器)
所有的并發(fā)編程面試題已合成文檔,由于篇幅過長(zhǎng),沒辦法全部上傳資料展示
知識(shí)腦圖:
忍不住想吐槽,知識(shí)點(diǎn)多且復(fù)雜。。。。。。。。。
快來,一起學(xué)習(xí),加入頭大家族吧!!
總結(jié)
以上是生活随笔為你收集整理的java 变量锁_并发编程高频面试题:可重入锁+线程池+内存模型等(含答案)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 微博批量发布,微博定时发布,批量删除,批
- 下一篇: java圆形进度条_可拖拽圆形进度条组件