并發(fā)編程面試(一)Synchronized 相關(guān)

Synchronized 原 理 是 什 么 ?

Synchronized 是 由 JVM 實 現(xiàn) 的 一 種 實 現(xiàn) 互 斥 同 步 的 一 種 方 式 , 如 果 你 查 看 被 Synchronized 修 飾 過 的 程 序 塊 編 譯 后 的 字 節(jié) 碼 , 會 發(fā) 現(xiàn) , 被 Synchronized 修 飾 過 的 程 序 塊 , 在 編 譯 前 后 被 編 譯 器 生 成 了 monitorenter 和 monitorexit 兩 個 字 節(jié) 碼 指 令 。

這 兩 個 指 令 是 什 么 意 思 呢 ?

在 虛 擬 機 執(zhí) 行 到 monitorenter 指 令 時 , 首 先 要 嘗 試 獲 取 對 象 的 鎖 :

如 果 這 個 對 象 沒 有 鎖 定 , 或 者 當(dāng) 前 線 程 已 經(jīng) 擁 有 了 這 個 對 象 的 鎖 , 把 鎖 的 計 數(shù) 器 + 1 ; 當(dāng) 執(zhí) 行 monitorexit 指 令 時 將 鎖 計 數(shù) 器 - 1 ; 當(dāng) 計 數(shù) 器 為 0 時 , 鎖 就 被 釋 放 了 。

如 果 獲 取 對 象 失 敗 了 , 那 當(dāng) 前 線 程 就 要 阻 塞 等 待 , 直 到 對 象 鎖 被 另 外 一 個 線 程 釋 放 為 止 。

Java 中 Synchronize 通 過 在 對 象 頭 設(shè) 置 標(biāo) 記 , 達(dá) 到 了 獲 取 鎖 和 釋 放 鎖 的 目 的 。

Synchronized 這 個 “ 鎖 ” 到 底 是 什 么 ? 如 何 確 定 對 象 的 鎖 ?

“ 鎖 ” 的 本 質(zhì) 其 實 是 monitorenter 和 monitorexit 字 節(jié) 碼 指 令 的 一 個 Reference 類 型 的 參 數(shù) , 即 要 鎖 定 和 解 鎖 的 對 象 。 我 們 知 道 , 使 用Synchronized 可 以 修 飾 不 同 的 對 象 , 因 此 , 對 應(yīng) 的 對 象 鎖 可 以 這 么 確定 。

  • 如 果 Synchronized 明 確 指 定 了 鎖 對 象 , 比 如 Synchronized( 變 量 名 ) 、 Synchronized( this) 等 , 說 明 加 解 鎖 對 象 為 該 對 象 。

  • 如 果 沒 有 明 確 指 定 :

    若 Synchronized 修 飾 的 方 法 為 非 靜 態(tài) 方 法 , 表 示 此 方 法 對 應(yīng) 的 對 象 為 鎖 對 象 ;

    若Synchronized 修 飾 的 方 法 為 靜 態(tài) 方 法 , 則 表 示 此 方 法 對 應(yīng) 的 類 對 象 為 鎖 對 象 。

  • 注 意 , 當(dāng) 一 個 對 象 被 鎖 住 時 , 對 象 里 面 所 有 用 Synchronized 修 飾 的 方 法 都 將 產(chǎn) 生 堵 塞 , 而 對 象 里 非 Synchronized 修 飾 的 方 法 可 正 常 被 調(diào) 用 , 不 受 鎖 影 響 。

什 么 是 可 重 入 性 , 為 什 么 說 Synchronized 是 可 重 入 鎖 ?

可 重 入 性 是 鎖 的 一 個 基 本 要 求 , 是 為 了 解 決 自 己 鎖 死 自 己 的 情 況 。

比 如 一 個 類 中 的 同 步 方 法 調(diào) 用 另 一 個 同 步 方 法 ,

假 如Synchronized 不 支 持 重 入 , 進(jìn) 入 method 2 方 法 時 當(dāng) 前 線 程 獲 得 鎖 , method 2 方 法 里 面 執(zhí) 行 method 1 時 當(dāng) 前 線 程 又 要 去 嘗 試 獲 取 鎖 , 這 時 如 果 不 支 持 重 入 , 它 就 要 等 釋 放 , 把 自 己 阻 塞 , 導(dǎo) 致 自 己 鎖 死 自 己 。

對 Synchronized 來 說 , 可 重 入 性 是 顯 而 易 見 的 , 剛 才 提 到 , 在 執(zhí) 行monitorenter 指 令 時 , 如 果 這 個 對 象 沒 有 鎖 定 , 或 者 當(dāng) 前 線 程 已 經(jīng) 擁有 了 這 個 對 象 的 鎖 ( 而 不 是 已 擁 有 了 鎖 則 不 能 繼 續(xù) 獲 取 ) , 就 把 鎖 的 計數(shù) 器 + 1 , 其 實 本 質(zhì) 上 就 通 過 這 種 方 式 實 現(xiàn) 了 可 重 入 性 。

JVM 對 Java 的 原 生 鎖 做 了 哪 些 優(yōu) 化 ?

在 Java 6 之 前 , Monitor 的 實 現(xiàn) 完 全 依 賴 底 層 操 作 系 統(tǒng) 的 互 斥 鎖 來 實 現(xiàn) , 也 就 是 我 們 剛 才 在 問 題 二 中 所 闡 述 的 獲 取 / 釋 放 鎖 的 邏 輯 。

由 于 Java 層 面 的 線 程 與 操 作 系 統(tǒng) 的 原 生 線 程 有 映 射 關(guān) 系 , 如 果 要 將 一 個 線 程 進(jìn) 行 阻 塞 或 喚 起 都 需 要 操 作 系 統(tǒng) 的 協(xié) 助 , 這 就 需 要 從 用 戶 態(tài) 切 換 到 內(nèi) 核 態(tài) 來 執(zhí) 行 , 這 種 切 換 代 價 十 分 昂 貴 , 很 耗 處 理 器 時 間 , 現(xiàn) 代 JDK 中 做 了 大 量 的 優(yōu) 化 。

一 種 優(yōu) 化 是 使 用 自 旋 鎖 , 即 在 把 線 程 進(jìn) 行 阻 塞 操 作 之 前 先 讓 線 程 自 旋 等 待 一 段 時 間 , 可 能 在 等 待 期 間 其 他 線 程 已 經(jīng) 解 鎖 , 這 時 就 無 需 再 讓 線 程 執(zhí) 行 阻 塞 操 作 , 避 免 了 用 戶 態(tài) 到 內(nèi) 核 態(tài) 的 切 換 。

現(xiàn) 代 JDK 中 還 提 供 了 三 種 不 同 的 Monitor 實 現(xiàn) , 也 就 是 三 種 不 同 的 鎖 :

· 偏 向 鎖 ( Biased Locking)

· 輕 量 級 鎖

· 重 量 級 鎖

這 三 種 鎖 使 得 JDK 得 以 優(yōu) 化 Synchronized 的 運 行 , 當(dāng) JVM 檢 測 到 不 同 的 競 爭 狀 況 時 , 會 自 動 切 換 到 適 合 的 鎖 實 現(xiàn) , 這 就 是 鎖 的 升 級 、 降 級 。

· 當(dāng) 沒 有 競 爭 出 現(xiàn) 時 , 默 認(rèn) 會 使 用 偏 向 鎖 。

JVM 會 利 用 CAS 操 作 , 在 對 象 頭 上 的 Mark Word 部 分 設(shè) 置 線 程ID, 以 表 示 這 個 對 象 偏 向 于 當(dāng) 前 線 程 , 所 以 并 不 涉 及 真 正 的 互 斥 鎖 , 因 為 在 很 多 應(yīng) 用 場 景 中 , 大 部 分 對 象 生 命 周 期 中 最 多 會 被 一 個 線 程 鎖 定 , 使 用 偏 斜 鎖 可 以 降 低 無 競 爭 開 銷 。

· 如 果 有 另 一 線 程 試 圖 鎖 定 某 個 被 偏 斜 過 的 對 象 , JVM就 撤 銷 偏 斜 鎖 , 切 換 到 輕 量 級 鎖 實 現(xiàn)

· 輕 量 級 鎖 依 賴 CAS 操 作 Mark Word 來 試 圖 獲 取 鎖 , 如 果 重 試 成 功 , 就 使 用 普 通 的 輕 量 級 鎖 ; 否 則 , 進(jìn) 一 步 升 級 為 重 量 級 鎖 。

為 什 么 說 Synchronized 是 非 公 平 鎖 ?

非 公 平 主 要 表 現(xiàn) 在 獲 取 鎖 的 行 為 上 , 并 非 是 按 照 申 請 鎖 的 時 間 前 后 給 等 待 線 程 分 配 鎖 的 , 每 當(dāng) 鎖 被 釋 放 后 , 任 何 一 個 線 程 都 有 機 會 競 爭 到 鎖 , 這 樣 做 的 目 的 是 為 了 提 高 執(zhí) 行 性 能 , 缺 點 是 可 能 會 產(chǎn) 生 線 程 饑 餓 現(xiàn) 象 。

什 么 是 鎖 消 除 和 鎖 粗 化 ?

· 鎖 消 除 : 指 虛 擬 機 即 時 編 譯 器 在 運 行 時 , 對 一 些 代 碼 上 要 求 同 步 , 但 被 檢 測 到 不 可 能 存 在 共 享 數(shù) 據(jù) 競 爭 的 鎖 進(jìn) 行 消 除 。 主 要 根 據(jù) 逃 逸 分 析 。

程 序 員 怎 么 會 在 明 知 道 不 存 在 數(shù) 據(jù) 競 爭 的 情 況 下 使 用 同 步 呢 ? 很 多 不 是 程 序 員 自 己 加 入 的

· 鎖 粗 化 : 原 則 上 , 同 步 塊 的 作 用 范 圍 要 盡 量 小 。 但 是 如 果 一 系 列 的 連 續(xù) 操 作 都 對 同 一 個 對 象 反 復(fù) 加 鎖 和 解 鎖 , 甚 至 加 鎖 操 作 在 循 環(huán) 體 內(nèi) , 頻 繁 地 進(jìn) 行 互 斥 同 步 操 作 也 會 導(dǎo) 致 不 必 要 的 性 能 損 耗 。

鎖 粗 化 就 是 增 大 鎖 的 作 用 域 。

為 什 么 說 Synchronized 是 一 個 悲 觀 鎖 ? 樂 觀 鎖 的 實 現(xiàn) 原 理

又 是 什 么 ? 什 么 是 CAS, 它 有 什 么 特 性 ?

Synchronized 顯 然 是 一 個 悲 觀 鎖 , 因 為 它 的 并 發(fā) 策 略 是 悲 觀 的 :

不 管 是 否 會 產(chǎn) 生 競 爭 , 任 何 的 數(shù) 據(jù) 操 作 都 必 須 要 加 鎖 、 用 戶 態(tài) 核 心 態(tài) 轉(zhuǎn) 換 、 維 護(hù) 鎖 計 數(shù) 器 和 檢 查 是 否 有 被 阻 塞 的 線 程 需 要 被 喚 醒 等 操 作 。

隨 著 硬 件 指 令 集 的 發(fā) 展 , 我 們 可 以 使 用 基 于 沖 突 檢 測 的 樂 觀 并 發(fā) 策 略 。 先 進(jìn) 行 操 作 , 如 果 沒 有 其 他 線 程 征 用 數(shù) 據(jù) , 那 操 作 就 成 功 了 ;

如 果 共 享 數(shù) 據(jù) 有 征 用 , 產(chǎn) 生 了 沖 突 , 那 就 再 進(jìn) 行 其 他 的 補 償 措 施 。 這 種 樂 觀 的 并 發(fā) 策 略 的 許 多 實 現(xiàn) 不 需 要 線 程 掛 起 , 所 以 被 稱 為 非 阻 塞 同 步 。 樂 觀 鎖 的 核 心 算 法 是 CAS( Compareand Swap, 比 較 并 交 換 ) , 它 涉 及 到 三 個 操 作 數(shù) : 內(nèi) 存 值 、 預(yù) 期 值 、 新 值 。 當(dāng) 且 僅 當(dāng) 預(yù) 期 值 和 內(nèi) 存 值 相 等 時 才 將 內(nèi) 存 值 修 改 為 新 值 。

這 樣 處 理 的 邏 輯 是 , 首 先 檢 查 某 塊 內(nèi) 存 的 值 是 否 跟 之 前 我 讀 取 時 的 一樣 , 如 不 一 樣 則 表 示 期 間 此 內(nèi) 存 值 已 經(jīng) 被 別 的 線 程 更 改 過 , 舍 棄 本 次 操 作 , 否 則 說 明 期 間 沒 有 其 他 線 程 對 此 內(nèi) 存 值 操 作 , 可 以 把 新 值 設(shè) 置 給 此 塊 內(nèi) 存 。

CAS 具 有 原 子 性 , 它 的 原 子 性 由 CPU 硬 件 指 令 實 現(xiàn) 保 證 , 即 使 用 JNI 調(diào) 用 Native 方 法 調(diào) 用 由 C++ 編 寫 的 硬 件 級 別 指 令 , JDK 中 提 供 了 Unsafe 類 執(zhí) 行 這 些 操 作 。

樂 觀 鎖 一 定 就 是 好 的 嗎 ?

樂 觀 鎖 避 免 了 悲 觀 鎖 獨 占 對 象 的 現(xiàn) 象 , 同 時 也 提 高 了 并 發(fā) 性 能 , 但 它 也 有 缺 點 :

  1. 樂 觀 鎖 只 能 保 證 一 個 共 享 變 量 的 原 子 操 作 。 如 果 多 一 個 或 幾 個 變 量 , 樂觀 鎖 將 變 得 力 不 從 心 , 但 互 斥 鎖 能 輕 易 解 決 , 不 管 對 象 數(shù) 量 多 少 及 對 象 顆 粒 度 大 小 。

  2. 長 時 間 自 旋 可 能 導(dǎo) 致 開 銷 大 。 假 如 CAS 長 時 間 不 成 功 而 一 直 自 旋 , 會 給 CPU 帶 來 很 大 的 開 銷 。

  3. ABA問 題 。 CAS 的 核 心 思 想 是 通 過 比 對 內(nèi) 存 值 與 預(yù) 期 值 是 否 一 樣 而 判 斷 內(nèi) 存 值 是 否 被 改 過 , 但 這 個 判 斷 邏 輯 不 嚴(yán) 謹(jǐn) , 假 如 內(nèi) 存 值 原 來 是A,后 來 被 一 條 線 程 改 為 B, 最 后 又 被 改 成 了 A, 則 CAS 認(rèn) 為 此 內(nèi) 存 值 并 沒 有 發(fā) 生 改 變 , 但 實 際 上 是 有 被 其 他 線 程 改 過 的 , 這 種 情 況 對 依 賴 過 程 值 的 情 景 的 運 算 結(jié) 果 影 響 很 大 。 解 決 的 思 路 是 引 入 版 本 號 , 每 次 變 量 更 新 都 把 版 本 號 加 一 。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容