Synchronized 不管是面試仍是在平常的工做中都佔據很是重要的位置,也是每一個 Java程序猿在併發編程道路上必須邁過的坎。Synchronized往淺了問是一個簡單的基礎面試題,往深了問就是一個比較難的源碼底層原理題,簡單的就很少說了,今天就講講那些底層原理題。
面試
另外本人整理了20年面試題大全,包含spring、併發、數據庫、Redis、分佈式、dubbo、JVM、微服務等方面總結,下圖是部分截圖,須要的話點這裏點這裏,暗號CSDN。spring
1.Synchronized用過嗎,其原理是什麼 ?
這 是一 道 Java 面 試 中 幾 乎 百 分 百 會 問 到 的 問 題 , 因 爲 沒 有 任 何 寫 過 並 發 程 序 的 開發 者 會 沒 聽 說 或 者 沒 接 觸 過 Synchronized。數據庫
Synchronized 是 由 JVM 實 現 的 一 種 實 現 互 斥 同 步 的 一 種 方 式 , 如 果你 查 看 被 Synchronized 修 飾 過 的 程 序 塊 編 譯 後 的 字 節 碼 , 會 發 現 ,被 Synchronized 修 飾 過 的 程 序 塊 , 在 編 譯 前 後 被 編 譯 器 生 成 了monitorenter 和 monitorexit 兩 個 字 節 碼 指 令 。編程
這兩個指令是什麼意 思 呢 ?併發
在 虛 擬 機 執 行 到 monitorenter 指 令 時 , 首 先 要 嘗 試 獲 取 對 象 的 鎖 :
如 果 這 個 對 象 沒 有 鎖 定 , 或 者 當 前 線 程 已 經 擁 有 了 這 個 對 象 的 鎖 , 把 鎖的 計 數 器 +1; 當 執 行 monitorexit 指 令 時 將 鎖 計 數 器 -1; 當 計 數 器爲 0 時 , 鎖 就 被 釋 放 了 。
分佈式
如 果 獲 取 對 象 失 敗 了 , 那 當 前 線 程 就 要 阻 塞 等 待 , 直 到 對 象 鎖 被 另 外 一個 線 程 釋 放 爲 止 。
Java 中 Synchronize 通 過 在 對 象 頭 設 置 標 記 , 達 到 了 獲 取 鎖 和 釋 放鎖 的 目 的 。
微服務
2.你剛纔提到獲取對象的鎖,這個「鎖」究竟是什麼?如何肯定對象的鎖 ?
「 鎖 」 的 本 質 其 實 是 monitorenter 和 monitorexit 字 節 碼 指 令 的 一個 Reference 類 型 的 參 數 , 即 要 鎖 定 和 解 鎖 的 對 象 。 我 們 知 道 , 使 用Synchronized 可 以 修 飾 不 同 的 對 象 , 因 此 , 對 應 的 對 象 鎖 可 以 這 麼 肯定 。優化
- 如 果 Synchronized 明 確 指 定 了 鎖 對 象 , 比 如 Synchronized( 變 量名 ) 、Synchronized(this) 等 , 說 明 加 解 鎖 對 象 爲 該 對 象 。
- 如 果 沒 有 明 確 指 定 :若 Synchronized 修 飾 的 方 法 爲 非 靜 態 方 法 , 表 示 此 方 法 對 應 的 對 象 爲鎖 對 象 ;若 Synchronized 修 飾 的 方 法 爲 靜 態 方 法 , 則 表 示 此 方 法 對 應 的 類 對 象爲 鎖 對 象 。
注 意 , 當 一 個 對 象 被 鎖 住 時 , 對 象 裏 面 所 有 用 Synchronized 修 飾 的方 法 都 將 產 生 堵 塞 , 而 對 象 裏 非 Synchronized 修 飾 的 方 法 可 正 常 被調 用 , 不 受 鎖 影 響 。this
3.什麼是可重入性,爲何說Synchronized是可重入鎖 ?
可 重 入 性 是 鎖 的 一 個 基 本 要 求 , 是 爲 了 解 決 自 己 鎖 死 自 己 的 情 況 。spa
一 個 類 中 的 同 步 方 法 調 用 另 一 個 同 步 方 法 , 假 如Synchronized 不 支 持 重 入 , 進 入 method2 方 法 時 當 前 線 程 獲 得 鎖 ,method2 方 法 裏 面 執 行 method1 時 當 前 線 程 又 要 去 嘗 試 獲 取鎖 , 這時 如 果 不 支 持 重 入 , 它 就 要 等 釋 放 , 把 自 己 阻 塞 , 導 致 自 己 鎖 死 自 己 。
對 Synchronized 來 說 , 可 重 入 性 是 顯 而 易 見 的 , 剛 才 提 到 , 在 執 行monitorenter 指 令 時 , 如 果 這 個 對 象 沒 有 鎖 定 , 或 者 當 前 線 程 已 經 擁有 了 這 個 對 象 的 鎖 ( 而 不 是 已 擁 有 了 鎖 則 不 能 繼 續 獲 取 ) , 就 把 鎖 的 計數 器 +1, 其 實 本 質 上 就 通 過 這 種 方 式 實 現 了 可 重 入 性 。
4.JVM對Java的原生鎖作了哪些優化 ?
在 Java 6 之 前 , Monitor 的 實 現 完 全 依 賴 底 層 操 做 系 統 的 互 斥 鎖 來實 現 , 也 就 是 我 們 剛 才 在 問 題 二 中 所 闡 述 的 獲 取 /釋 放 鎖 的 邏 輯 。
由 於 Java 層 面 的 線 程 與 操 做 系 統 的 原 生 線 程 有 映 射 關 系 , 如 果 要 將 一個 線 程 進 行 阻 塞 或 喚 起 都 需 要 操 做 系 統 的 協 助 , 這 就 需 要 從 用 戶 態 切 換到 內 核 態 來 執 行 , 這 種 切 換 代 價 十 分 昂 貴 , 很 耗 處 理 器 時 間 , 現 代 JDK中 作 了 大 量 的 優 化 。
一 種 優 化 是 使 用 自 旋 鎖 , 即 在 把 線 程 進 行 阻 塞 操 做 之 前 先 讓 線 程 自 旋 等待 一 段 時 間 , 可 能 在 等 待 期 間 其 他 線 程 已 經 解 鎖 , 這 時 就 無 需 再 讓 線 程執 行 阻 塞 操 做 , 避 免 了 用 戶 態 到 內 核 態 的 切 換 。
現 代 JDK 中 還 提 供 了 三 種 不 同 的 Monitor 實 現 , 也 就 是 三 種 不 同 的鎖 :
- 偏 向 鎖 ( Biased Locking)
- 輕 量 級 鎖
- 重 量 級 鎖
這 三 種 鎖 使 得 JDK 得 以 優 化 Synchronized 的 運 行 , 當 JVM 檢 測到 不 同 的 競 爭 狀 況 時 , 會 自 動 切 換 到 適 合 的 鎖 實 現 , 這 就 是 鎖 的 升 級 、降 級 。
- 當 沒 有 競 爭 出 現 時 , 默 認 會 使 用 偏 向 鎖 。
JVM 會 利 用 CAS 操 做 , 在 對 象 頭 上 的 Mark Word 部 分 設 置 線 程ID, 以 表 示 這 個 對 象偏 向 於 當 前 線 程 , 所 以 並 不 涉 及 真 正 的 互 斥 鎖 , 由於 在 很 多 應 用 場 景 中 , 大 部 分 對象 生 命 周 期 中 最 多 會 被 一 個 線 程 鎖 定 ,使 用 偏 斜 鎖 可 以 降 低 無 競 爭 開 銷 。
- 如 果 有 另 一 線 程 試 圖 鎖 定 某 個 被 偏 斜 過 的 對 象 , JVM 就 撤 銷 偏 斜 鎖 ,切 換 到 輕 量 級 鎖 實 現 。
- 輕 量 級 鎖 依 賴 CAS 操 做 Mark Word 來 試 圖 獲 取 鎖 , 如 果 重 試 成 功 ,
就 使 用 普 通 的 輕 量 級 鎖 ; 否 則 , 進 一 步 升 級 爲 重 量 級 鎖 。
5.爲何說Synchronized是非公平鎖 ?
非 公 平 主 要 表 現 在 獲 取 鎖 的 行 爲 上 , 並 非 是 按 照 申 請 鎖 的 時 間 前 後 給 等待 線 程 分 配 鎖 的 , 每 當 鎖 被 釋 放 後 , 任 何 一 個 線 程 都 有 機 會 競 爭 到 鎖 ,這 樣 作 的 目 的 是 爲 了 提 高 執 行 性 能 , 缺 點 是 可 能 會 產 生 線 程 飢 餓 現 象 。
6.什麼是鎖消除和鎖粗化 ?
-
鎖 消 除 : 指 虛 擬 機 即 時 編 譯 器 在 運 行 時 , 對 一 些 代 碼 上 要 求 同 步 , 但 被檢 測 到 不 可 能 存 在 共 享 數 據 競 爭 的 鎖 進 行 消 除 。 主 要 根 據 逃 逸 分 析 。程 序 員 怎 麼 會 在 明 知 道 不 存 在 數 據 競 爭 的 情 況 下 使 用 同 步 呢? 很 多 不 是程 序 員 自 己 加 入 的 。
-
鎖 粗 化 : 原 則 上 , 同 步 塊 的 做 用 範 圍 要 盡 量 小 。 但 是 如 果 一 系 列 的 連 續操 做 都 對 同 一 個 對 象 反 復 加 鎖 和 解 鎖 , 甚 至 加 鎖 操 做 在 循 環 體 內 , 頻 繁地 進 行 互 斥 同 步 操 做 也 會 導 致 不 必 要 的 性 能 損 耗 。鎖 粗 化 就 是 增 大 鎖 的 做 用 域 。
7.爲何說Synchronized是一個悲觀鎖?樂觀鎖的實現原理又是什麼?什麼是CAS,它有什麼特性 ?
Synchronized 顯 然 是 一 個 悲 觀 鎖 , 因 爲 它 的 並 發 策 略 是 悲 觀 的 :
不 管 是 否 會 產 生 競 爭 , 任 何 的 數 據 操 做 都 必 須 要 加 鎖 、 用 戶 態 核 心 態 轉換 、 維 護 鎖 計 數 器 和 檢 查 是 否 有 被 阻 塞 的 線 程 需 要 被 喚 醒 等 操 做 。
隨 着 硬 件 指 令 集 的 發 展 , 我 們 可 以 使 用 基 於 衝 突 檢 測 的 樂 觀 並 發 策 略 。先 進 行 操 做 , 如 果 沒 有 其 他 線 程 徵 用 數 據 , 那 操 做 就 成 功 了 ;
如 果 共 享 數 據 有 徵 用 , 產 生 了 衝 突 , 那 就 再 進 行 其 他 的 補 償 措 施 。 這 種樂 觀 的 並 發 策 略 的 許 多 實 現 不 需 要 線 程 掛 起 , 所 以 被 稱 爲 非 阻 塞 同 步 。
樂 觀 鎖 的 核 心 算 法 是 CAS( Compareand Swap, 比 較 並 交 換 ) , 它 涉及 到 三 個 操 做 數 : 內 存 值 、 預 期 值 、 新 值 。 當 且 僅 當 預 期 值 和 內 存 值 相等 時 才 將 內 存 值 修 改 爲 新 值 。
這 樣 處 理 的 邏 輯 是 , 首 先 檢 查 某 塊 內 存 的 值 是 否 跟 之 前 我 讀 取 時 的 同樣, 如 不 一 樣 則 表 示 期 間 此 內 存 值 已 經 被 別 的 線 程 更 改 過 , 舍 棄 本 次 操做 , 否 則 說 明 期 間 沒 有 其 他 線 程 對 此 內 存 值 操 做 , 可 以 把 新 值 設 置 給 此塊 內 存 。
CAS 具 有 原 子 性 , 它 的 原 子 性 由 CPU 硬 件 指 令 實 現 保 證 , 即 使 用JNI 調 用 Native 方 法 調 用 由 C++ 編 寫 的 硬 件 級 別 指 令 , JDK 中 提供 了 Unsafe 類 執 行 這 些 操 做 。
8.樂觀鎖必定就是好的嗎?
樂 觀 鎖 避 免 了 悲 觀 鎖 獨 佔 對 象 的 現 象 , 同 時 也 提 高 了 並 發 性 能 , 但 它 也有 缺 點 :
- 樂 觀 鎖 只 能 保 證 一 個 共 享 變 量 的 原 子 操 做 。 如 果 多 一 個 或 幾 個 變量, 樂觀 鎖 將 變 得 力 不 從 心 , 但 互 斥 鎖 能 輕 易 解 決 , 不 管 對 象 數 量 多 少 及 對 象顆 粒 度 大 小 。
- 長 時 間 自 旋 可 能 導 致 開 銷 大 。 假 如 CAS 長 時 間 不 成 功 而 一 直 自 旋 , 會給 CPU 帶 來 很 大 的 開 銷 。
- ABA 問 題 。 CAS 的 核 心 思 想 是 通 過 比 對 內 存 值 與 預 期 值 是 否 一 樣 而 判斷 內 存 值 是 否 被 改 過 , 但 這 個 判 斷 邏 輯 不 嚴 謹 , 假 如 內 存 值 原 來 是 A,後 來 被 一 條 線 程 改 爲 B, 最 後 又 被 改 成 了 A, 則 CAS 認 爲 此 內 存 值 並沒 有 發 生 改 變 , 但 實 際 上 是 有 被 其 他 線 程 改 過 的 , 這 種 情 況 對 依 賴 過 程值 的 情 景 的 運 算 結 果 影 響 很 大 。 解 決 的 思 路 是 引 入 版 本號, 每 次 變 量 更新 都 把 版 本 號 加 一 。
最後:
針對最近不少人都在面試,我這邊也整理了至關多的面試專題資料,也有其餘大廠的面經。但願能夠幫助到你們。
下面的面試題答案都整理成文檔筆記。也還整理了一些面試資料&最新2020收集的一些大廠的面試真題(都整理成文檔,小部分截圖),有須要的能夠點擊進入暗號CSDN