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