【学习笔记】juc并发学习+关于锁的面试题
文章目錄
- 查看鎖的信息
- 饑餓死鎖的例子
- Synchronized 其 原 理 是 什 么 ?
- 你 剛 才 提 到 獲 取 對(duì) 象 的 鎖 ,這 個(gè)“ 鎖 ”到 底 是 什 么 ? 如 何 確 定 對(duì)象的鎖
- 什 么 是 可 重 入 性 , 為 什 么 說 Synchronized 是 可 重 入 鎖 ?
- 為 什 么 說 Synchronized 是 一 個(gè) 悲 觀 鎖 ? 樂 觀 鎖 的 實(shí) 現(xiàn) 原 理 又 是 什 么 ? 什 么 是 CAS, 它 有 什 么 特 性 ?
- 樂 觀 鎖 一 定 就 是 好 的 嗎 ?
- 跟 Synchronized 相 比 , 可 重 入 鎖 ReentrantLock 其實(shí)現(xiàn)原理 有 什 么 不 同 ?
- AQS框 架 是 怎 么 回 事 兒 ?
- synchronized實(shí)例
查看鎖的信息
jvisualvm
查看pid
死鎖代碼:
C:\hello>jstack -l 20488
饑餓死鎖的例子
public class ExecutorLock { private static ExecutorService single = Executors.newSingleThreadExecutor(); public static class AnotherCallable implements Callable<String> { @Override public String call() throws Exception { System.out.println("in AnotherCallable"); return "annother success"; } }public static class MyCallable implements Callable<String> { @Override public String call() throws Exception { System.out.println("in MyCallable"); Future<String> submit = single.submit(new AnotherCallable()); return "success:" + submit.get(); } } public static void main(String[] args) throws ExecutionException, InterruptedException { MyCallable task = new MyCallable(); Future<String> submit = single.submit(task); System.out.println(submit.get()); System.out.println("over"); single.shutdown(); } }Synchronized 其 原 理 是 什 么 ?
Synchronized 是 由 JVM 實(shí) 現(xiàn) 的 一 種 實(shí) 現(xiàn) 互 斥 同 步 的 一 種 方 式 , 如 果你查看被 Synchronized 修 飾 過 的 程 序 塊 編 譯 后 的 字 節(jié) 碼 , 會(huì) 發(fā) 現(xiàn) , 被Synchronized 修 飾 過 的 程 序 塊 , 在 編 譯 前 后 被 編 譯 器 生 成了 monitorenter 和 monitorexit 兩 個(gè) 字 節(jié) 碼 指 令 。
這 兩 個(gè) 指 令 是 什 么 意 思 呢 ?
在 虛 擬 機(jī) 執(zhí) 行 到 monitorenter 指 令 時(shí) , 首 先 要 嘗 試 獲 取 對(duì) 象 的 鎖 :
如 果 這 個(gè) 對(duì) 象 沒 有 鎖 定 ,或 者 當(dāng) 前 線 程 已 經(jīng) 擁 有 了 這 個(gè) 對(duì) 象 的 鎖 ,把 鎖 的計(jì)數(shù)器 +1;當(dāng) 執(zhí) 行 monitorexit 指 令 時(shí) 將 鎖 計(jì) 數(shù) 器 -1;當(dāng) 計(jì) 數(shù) 器 為 0 時(shí) , 鎖 就 被 釋 放 了 。
如 果 獲 取 對(duì) 象 失 敗 了 ,那 當(dāng) 前 線 程 就 要 阻 塞 等 待 ,直 到 對(duì) 象 鎖 被 另 外 一 個(gè)線 程 釋 放 為 止 。
Java 中 Synchronize 通 過 在 對(duì) 象 頭 設(shè) 置 標(biāo) 記 , 達(dá) 到 了 獲 取 鎖 和 釋 放 鎖的目的。
你 剛 才 提 到 獲 取 對(duì) 象 的 鎖 ,這 個(gè)“ 鎖 ”到 底 是 什 么 ? 如 何 確 定 對(duì)象的鎖
“ 鎖 ” 的 本 質(zhì) 其 實(shí) 是 monitorenter 和 monitorexit 字 節(jié) 碼 指 令 的 一 個(gè)
Reference 類 型 的 參 數(shù) , 即 要 鎖 定 和 解 鎖 的 對(duì) 象 。 我 們 知 道 , 使 用Synchronized 可 以 修 飾 不 同 的 對(duì) 象 ,因 此 ,對(duì) 應(yīng) 的 對(duì) 象 鎖 可 以 這 么 確 定 。
若 Synchronized 修 飾 的 方 法 為 非 靜 態(tài) 方 法 ,表 示 此 方 法 對(duì) 應(yīng) 的 對(duì) 象 為 鎖對(duì)象;
若 Synchronized 修 飾 的 方 法 為 靜 態(tài) 方 法 ,則 表 示 此 方 法 對(duì) 應(yīng) 的 類 對(duì) 象 為鎖對(duì)象。
注 意 , 當(dāng) 一 個(gè) 對(duì) 象 被 鎖 住 時(shí) , 對(duì)象里面有用Synchronized 修飾的方法 都 將 產(chǎn) 生 堵 塞 , 而 對(duì) 象 里 非 Synchronized 修 飾 的 方 法 可 正 常 被 調(diào) 用 ,不 受 鎖 影 響 。
什 么 是 可 重 入 性 , 為 什 么 說 Synchronized 是 可 重 入 鎖 ?
可 重 入 性 是 鎖 的 一 個(gè) 基 本 要 求 , 是 為 了 解 決 自 己 鎖 死 自 己 的 情 況 。
比 如一 個(gè) 類 中 的 同 步 方 法 調(diào) 用 另 一 個(gè) 同 步 方 法 , 假 如
Synchronized 不 支 持 重 入 , 進(jìn) 入 method2 方 法 時(shí) 當(dāng) 前 線 程 獲 得 鎖 ,method2 方 法 里 面 執(zhí) 行 method1 時(shí) 當(dāng) 前 線 程 又 要 去 嘗 試 獲 取 鎖 , 這時(shí) 如 果 不 支 持 重 入 , 它 就 要 等 釋 放 , 把 自 己 阻 塞 , 導(dǎo) 致 自 己 鎖 死 自 己 。
為 什 么 說 Synchronized 是 一 個(gè) 悲 觀 鎖 ? 樂 觀 鎖 的 實(shí) 現(xiàn) 原 理 又 是 什 么 ? 什 么 是 CAS, 它 有 什 么 特 性 ?
Synchronized 顯 然 是 一 個(gè) 悲 觀 鎖 , 因 為 它 的 并 發(fā) 策 略 是 悲 觀 的 :
不 管 是 否 會(huì) 產(chǎn) 生 競 爭 ,任 何 的 數(shù) 據(jù) 操 作 都 必 須 要 加 鎖 、用 戶 態(tài) 核 心 態(tài) 轉(zhuǎn) 換 、維 護(hù) 鎖 計(jì) 數(shù) 器 和 檢 查 是 否 有 被 阻 塞 的 線 程 需 要 被 喚 醒 等 操 作 。
隨 著 硬 件 指 令 集 的 發(fā) 展 ,我 們 可 以 使 用 基 于 沖 突 檢 測 的 樂 觀 并 發(fā) 策 略 。先進(jìn) 行 操 作 , 如 果 沒 有 其 他 線 程 征 用 數(shù) 據(jù) , 那 操 作 就 成 功 了 ;如 果 共 享 數(shù) 據(jù) 有 征 用 ,產(chǎn) 生 了 沖 突 ,那 就 再 進(jìn) 行 其 他 的 補(bǔ) 償 措 施 。這 種 樂觀 的 并 發(fā) 策 略 的 許 多 實(shí) 現(xiàn) 不 需 要 線 程 掛 起 , 所 以 被 稱 為 非 阻 塞 同 步 。
樂 觀 鎖 的 核 心 算 法 是 CAS( Compareand Swap,比較并交換 ) , 它 涉及 到 三 個(gè) 操 作 數(shù) :內(nèi) 存 值 、預(yù) 期 值 、新 值 。當(dāng) 且 僅 當(dāng) 預(yù) 期 值 和 內(nèi) 存 值 相 等時(shí)才將內(nèi)存值改為新值,這 樣 處 理 的 邏 輯 是 , 首 先 檢 查 某 塊 內(nèi) 存 的 值 是 否 跟 之 前 我 讀 取 時(shí) 的 一 樣 ,如 不 一 樣 則 表 示 期 間 此 內(nèi) 存 值 已 經(jīng) 被 別 的 線 程 更 改 過 ,舍 棄 本 次 操 作 ,否
則 說 明 期 間 沒 有 其 他 線 程 對(duì) 此 內(nèi) 存 值 操 作 , 可 以 把 新 值 設(shè) 置 給 此 塊 內(nèi) 存 。
CAS 具 有 原 子 性 ,它 的 原 子 性 由 CPU 硬 件 指 令 實(shí) 現(xiàn) 保 證 ,即 使 用 JNI 調(diào) 用 Native 方 法 調(diào) 用 由 C++ 編 寫 的 硬 件 級(jí) 別 指 令 , JDK 中 提 供 了Unsafe 類 執(zhí) 行 這 些 操 作 。
樂 觀 鎖 一 定 就 是 好 的 嗎 ?
樂 觀 鎖 避 免 了 悲 觀 鎖 獨(dú) 占 對(duì) 象 的 現(xiàn) 象 ,同 時(shí) 也 提 高 了 并 發(fā) 性 能 ,但 它 也 有缺點(diǎn):
可重入鎖 ReentrantLock 及 其他 顯 式 鎖相 關(guān) 問題
跟 Synchronized 相 比 , 可 重 入 鎖 ReentrantLock 其實(shí)現(xiàn)原理 有 什 么 不 同 ?
其 實(shí) , 鎖 的 實(shí) 現(xiàn) 原 理 基 本 是 為 了 達(dá) 到 一 個(gè) 目 的 :
讓 所 有 的 線 程 都 能 看 到 某 種 標(biāo) 記 。
Synchronized 通過在對(duì)象頭中設(shè)置標(biāo)記實(shí)現(xiàn)了這一目的,是一種 JVM
原 生 的 鎖 實(shí) 現(xiàn) 方 式 , 而 ReentrantLock 以 及 所 有 的 基 于 Lock 接 口 的實(shí) 現(xiàn) 類 ,都 是 通 過 用 一 個(gè) volitile 修飾的 int 型 變 量 ,并 保 證 每 個(gè) 線 程都 能 擁 有 對(duì) 該 int 的 可 見 性 和 原 子 修 改 ,其 本 質(zhì) 是 基 于 所 謂 的 AQS 框架 。
AQS框 架 是 怎 么 回 事 兒 ?
AQS( AbstractQueuedSynchronizer 類 ) 是 一 個(gè) 用 來 構(gòu) 建 鎖 和 同 步 器的 框 架 , 各 種Lock 包 中 的 鎖 ( 常 用 的 有ReentrantLock 、ReadWriteLock) , 以 及 其 他 如 Semaphore、 CountDownLatch, 甚至 是 早 期 的 FutureTask 等 , 都 是 基 于 AQS 來構(gòu)建。
5. AQS 在 內(nèi) 部 定 義 了 一 個(gè) volatile int state 變 量 , 表 示 同 步 狀 態(tài) : 當(dāng) 線 程調(diào) 用 lock 方法時(shí) ,如 果 state=0,說 明 沒 有 任 何 線 程 占 有 共 享 資 源 的 鎖 ,可 以 獲 得 鎖 并 將 state=1;如果 state=1, 則 說 明 有 線 程 目 前 正 在 使 用 共享 變 量 , 其 他 線 程 必 須 加 入 同 步 隊(duì) 列 進(jìn) 行 等 待 。
6. AQS 通 過 Node 內(nèi) 部 類 構(gòu) 成 的 一 個(gè) 雙 向 鏈 表 結(jié) 構(gòu) 的 同 步 隊(duì) 列 , 來 完 成 線程 獲 取 鎖 的 排 隊(duì) 工 作 , 當(dāng) 有 線 程 獲 取 鎖 失 敗 后 , 就 被 添 加 到 隊(duì) 列 末 尾 。
Node 類 是 對(duì) 要 訪 問 同 步 代 碼 的 線 程 的 封 裝 , 包 含 了 線 程 本 身 及 其 狀 態(tài) 叫waitStatus( 有 五 種 不 同 取 值 , 分 別 表 示 是 否 被 阻 塞 , 是 否 等 待 喚 醒 , 是否 已 經(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ì) 等 待( 如 ReentranLock) 。
7. AQS 通 過 內(nèi) 部 類 ConditionObject 構(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) 行 鎖 競 爭 。
synchronized實(shí)例
1.synchronized:內(nèi)置的Java關(guān)鍵字
LOCK:一個(gè)接口,下面有多個(gè)實(shí)現(xiàn)類,可以判斷是否取得了鎖
2.synchronized自動(dòng)釋放鎖,lock必須手動(dòng)釋放鎖
3.synchronized不可以中斷,非公平鎖,lock 可以設(shè)置公平還是非公平
前者適合鎖少量代碼同步問題,后者適合鎖大量同步代碼
一個(gè)用synchronized的例子1:
1this.notify()去掉this對(duì)結(jié)果無影響
以上例子如果再加一個(gè)線程C,改成notifyAll()會(huì)出現(xiàn)問題:
A->1
B->0
A->1
B->0
C->1
A->2
C->3
B->2
B->1
B->0
C->1
A->2
發(fā)現(xiàn)輸出不正確了,是因?yàn)?if和while的虛假喚醒問題 ,改if為while
增加CD兩個(gè)線程:
輸出
A->1
B->0
A->1
B->0
A->1
B->0
A->1
B->0
A->1
B->0
C->1
D->0
C->1
D->0
C->1
D->0
C->1
D->0
C->1
D->0
五個(gè)為一輪
例子2:
public class Test {public static void main(String[] args) {Sell sell = new Sell();new Thread(() -> {sell.sell1();},"A").start();new Thread(() -> {sell.sell2();},"B").start(); } } class Sell{public synchronized void sell1(){System.out.println("賣衣服");}public synchronized void sell2(){System.out.println("賣包子");} }輸出賣衣服
賣包子
如果sell2和sell1改一下順序,則輸出變成賣包子 賣衣服
說明:sychronized鎖的對(duì)象是方法的調(diào)用者,由于上面兩個(gè)方法用的是同一個(gè)鎖,因此誰先拿到鎖先執(zhí)行誰
加了sleep方法:
賣衣服B
賣包子A
賣包子B
賣衣服A
記住:非同步方法不受鎖的影響
如果sychronized修飾的方法是static的,則鎖的是整個(gè)class,此時(shí)即使即使new了不同的兩個(gè)實(shí)例,仍然是同一把鎖
總結(jié)
以上是生活随笔為你收集整理的【学习笔记】juc并发学习+关于锁的面试题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【算法】设计算法求所有强连通分量的完整代
- 下一篇: 【过程记录】aop学习·实现动态代理的j