主頁 > 軟體設計 > 《重學Java高并發》你管這“破玩意兒”叫鎖(沒有高并發經驗的朋友們看過來,該專欄結合筆者的實戰來講高并發)

《重學Java高并發》你管這“破玩意兒”叫鎖(沒有高并發經驗的朋友們看過來,該專欄結合筆者的實戰來講高并發)

2021-09-15 10:31:44 軟體設計

專欄特色:結合10余年的作業經驗,在實踐中提煉總結高并發經驗,將理論落到實處,不僅助力面試,更是真正提高技能,
專欄目錄:

  1. 《重學Java高并發》Sempahore的使用場景與常見誤區
  2. 《重學Java高并發》手寫一個生產者消費者執行緒模型

正確理解鎖是深入理解Java并發的重中之重,

接下來和筆者一步一步進入"Java的鎖世界"中來吧,本文將循序漸進的介紹鎖的相關知識,從簡單到難,從概念到實踐思路,

1、鎖的種類

首先以一個非常常見的生活場景舉例,例如一個三口之家居住在一個二房一廳的房子里,只有一個衛生間,早上一起床,大家是不是都有搶衛生間,這里就會發生一個有意思的事情了,一人在如廁,其他人排隊等待的場景,

在這里插入圖片描述
這個場景下有如下幾個關鍵的特征:

  • 獨占
    “廁所“作為一個資源,在任意時刻只能被一個人占用,為了實作該效果,使用資源之前,需要先獲得與該資源關聯的鎖
  • 當多個執行緒都需要訪問該資源時,必須先獲得鎖,而且在同一時刻有且只會有一個執行緒獲得鎖,那沒有獲得鎖的執行緒就需要排隊等待,是一直等,還是等得不耐煩時就放棄?
  • 當有多人排隊時,一個執行緒將鎖釋放后,交給誰?什么樣的策略?

上面是最常見的鎖應用場景,有一個非常響亮的名稱:互斥鎖、排它鎖

1.1 互斥鎖

在java領域中,實作互斥鎖通常有兩種方式:

  • synchronized
  • ReentrantLock

接下來對比兩者的不同點,從而來了解互斥鎖的基本語意

  • 可重入性
    所謂的可重入性一個執行緒獲取鎖后,沒有釋放之前,繼續申請,其偽代碼如下所示:
    在這里插入圖片描述
    synchronized與ReentrantLock都支持可重入性,

  • 鎖只能被鎖的擁有者釋放
    大家如何基于redis實作分布式鎖時,要特別注意這個特質,

  • 申請鎖時是否支持超時取消
    申請鎖的時候,ReentrantLock能支持設定超時時間,即呼叫申請鎖時如果在指定時間內未獲取鎖,支持自動停止阻塞跳出,而synchronized不支持

  • 申請鎖時是否支持被中斷
    ReentrantLock可以通過呼叫lockInterruptibly方法,可以支持執行緒中斷,即停止繼續申請鎖,同樣synchronized不支持

  • 是否支持公平鎖/非公平鎖
    所謂的公平鎖,是指當擁有鎖的執行緒釋放鎖后,鎖的下一個獲取者就是鎖等待佇列中的第一個元素,而非公平鎖并沒有這個限制,ReentrantLock支持,而synchronized不支持,

1.2 共享鎖

與互斥鎖相對應的是共享鎖,所謂的共享鎖是同一時間可以被多個執行緒共同申請,一個非常經典的使用場景就是讀寫鎖,

例如在一個快取場景,在一個商品系統中,為了提供對商品的訪問性能,通常會引入一個快取區(Map)來快取商品的資料,快取資料對查詢請求(讀請求)是可以并行執行的,即多個執行緒同時查詢快取區的資料,這個是一個非常安全的操作,但不允許多個執行緒對快取區進行修改,這里共享鎖的意義就發揮出來了,

既然多個執行緒對快取區可以同時進行讀操作,那為什么還要加共享鎖呢?主要的目的是避免寫操作與讀操作同時進行

只要當前有讀操作在進行,寫操作就需要排隊,請看如下示例圖:

在這里插入圖片描述
如上圖所示:例如 執行緒T1,T2,T3連續申請共享鎖,然后T4申請寫鎖,再T5申請讀鎖,那各個執行緒的并發執行情況如下所示:

  • 執行緒 T1、T2、T3 將并發執行
  • T4由于是申請的寫鎖,必須等 T1、T2、T3釋放鎖后,才能執行,
  • T5雖然申請是共享鎖,但由于T4持有寫鎖,故T5也需要阻塞,直至T4釋放鎖,

在Java等世界中按鎖的排斥性來分基本就包含排它鎖與共享鎖,其他讀寫鎖、間隙鎖等是以鎖的粒度這個緯度進行細分,

2、鎖的實作原理

在了解了鎖的基本語意義之后,我們有必要來闡述一下鎖的實作原理,

從某種意義上來說,鎖的實作原理就是兩個佇列:同步阻塞佇列、條件等待佇列

2.1 阻塞佇列

阻塞佇列的作用說明如下圖所示:
在這里插入圖片描述
上面使用來synchronized,其傳入的是一個鎖物件,如果此時有5個執行緒同時去執行這段代碼,由于鎖的互斥性,同一時間只有一個執行緒能獲得鎖,其他執行緒需要排隊等待,故需要引入一個佇列來存盤在這些排隊的執行緒,所以synchronized的實作機制中,會在鎖物件中開辟一個佇列,用來存盤等待獲取當前鎖的執行緒

2.2 條件等待佇列

Object物件中有一對特殊的方法:wait()/notify()/notifyAll(),大家在前文中應該看到消費者/生產者中示例中,使用過wait,notify方法,示例代碼如下:
在這里插入圖片描述
wait方法必須在synchronized中呼叫,并且通常是執行緒呼叫鎖物件的wait方法,表示當前繼續往下執行的條件不足,當前執行緒需要等待,故需要為鎖物件再維護一個個佇列,用來存盤等待的執行緒,俗稱條件等待佇列

當其他執行緒呼叫鎖物件的notify方法或notifyAll方法,會喚醒等待佇列中的執行緒,

溫馨提示:上述還有幾個關鍵點:

  • Object.wait方法,會使當前執行緒進入等待狀態,并且釋放鎖
  • 通常條件等待會使用while陳述句,避免條件不滿足時被誤喚醒,故使用while對條件進行再一次的判斷,
  • 當被喚醒后,并不立即去執行while條件判斷,而是需要重新去申請鎖,即可能會進入到阻塞佇列

3、鎖的優化思路

我相信作為一個程式員,大家都對鎖很敏感,因為性能低下,但鎖肯定有其存在的原因,主要解決資料訪問的安全性,大家可能會感到驚訝,作為一款高性能的訊息中間件(RocketMQ),在訊息寫入時也使用了鎖,其代碼如下:
在這里插入圖片描述
這是因為RocketMQ是順序寫檔案,多個請求同時申請寫一個檔案,必須排隊執行,否則會帶來邏輯例外,此時鎖是不用不行了,

對鎖的優化策略,通常基于如下原則:能不用鎖就不使用鎖,必須使用鎖則盡量保證被鎖包裹代碼的快速執行、降低鎖的粒度,

3.1 優化鎖執行時間

當然能不用鎖就不用鎖,但有些場景是必須使用鎖來保證多執行緒環境下結果的正確性,就以RocketMQ順序寫commitlog檔案為例,對同一個檔案寫入,需要記錄當前的寫入位置,然后另外一個執行緒就進行追加,故這個為寫入位置是多執行緒不安全的,故必須引入鎖,那RocketMQ作為一款高性能的訊息中間件,是如何做到訊息發送的高并發,低延遲能力低呢?

核心法寶:控制鎖的范圍,確保被鎖包含的代碼執行性能高效,接下來我們看一下RocketMQ訊息寫入的幾個重要步驟:
在這里插入圖片描述
并不是需要將上述三個步驟都加鎖,而是只對寫記憶體這段加鎖即可,這段代碼非常高效,

3.2 優化鎖的粒度

鎖的性能優化是一個永恒的主旨,另外一個核心思路是:降低鎖的粒度,提高并發度

接下來我們以JDK中的HashTable與ConcurrentHashMap的實作原理為例,讓大家體會一下如何降低鎖的粒度從而提高并發度

Hashtable的性能低下是眾所周知,因為整個容器就一把鎖,因為它的get、put都是被synchronized修飾,synchronized用來修飾非static方法,其鎖物件為Hashtable是物件鎖,

并發度:同一時間只有一個執行緒能向該容器添加資料、獲取資料,

而jkd1.7及其版本,ConcurrentHashMap的內部資料結構如下圖所示:
在這里插入圖片描述
可以看出ConcurrentHashMap的設計思路是將整個HashMap分割成多個小的HashMap,然后為每一個HashMap加鎖,從而降低鎖的粒度,從而提高并發度

在JDK1.8及版本后,ConcurrentHashMap的存盤結構又發了很大改變,摒棄分段思想,使用來陣列 + Node ,進一步釋放讀寫的并發度,其資料結構如下圖所示:

在這里插入圖片描述
其中,對每一個鏈表的Node節點,寫操作時會加鎖,但在查詢時候,并不會對各個Node加鎖,提高讀操作的并發度;并且會基于CAS機制實作無鎖化處理,使用volatile保證可見性,

本文并不準備去剖析ConcurrentHashMap的實作細節,后續專門從原始碼實作的角度深度剖析,敬請期待,也請持續關注我,

3.3 無鎖化設計

鎖的存在必然有其使用場景,特別是需要被鎖保護的資源眾多,即臨界區中的邏輯復雜,對其進行拆分會使代碼變的臃腫,直接使用鎖保護會清晰明了,但評估是否需要引入鎖時需要慎重,特別是一些對吞吐量有極高要求的場景,能不用鎖就不要用鎖.

無鎖化設計的基礎:CAS,比較和交換,

在Java領域也提供了對應的原子操作工具:CAS,CAS 操作包含三個運算元 —— 記憶體位置(V)、預期原值(A)和新值(B), 如果記憶體位置的值與預期原值相匹配,那么處理器會自動將該位置值更新為新值 ,否則,處理器不做任何操作,CAS是CPU指令級命令

CAS簡單使用示例如下:
在這里插入圖片描述
正如筆者在優化信號量釋放邏輯時引入了cas確保一個SempahoreReleaseOnlyOne只會釋放一次信號量,

在JUC框架中的ArrayBlockingQueue,LinkedBlockingQueue等佇列都支持多個執行緒同時往佇列中寫入資料,但其內部都引入了鎖,

多個執行緒往佇列中寫入資料,一定要加鎖?怎么進行無鎖化設計呢?

Disruptor框架,實作多執行緒環境中真正的無鎖化設計,極大的提升并發性能,提供了多個執行緒可以同時并發安全的往同一佇列寫入資料,而不加鎖,是不是很神奇?

由于篇幅的原因,本文并不會為大家揭曉Disruptor是如何實作多個執行緒在不引入鎖的情況下對佇列進行并發操作的,興趣是最好的老師,如果大家有興趣可以先提前研究,后續將在《重學Java高并發》系列中后續文章中專門詳細剖析,


一鍵三連(關注、點贊、留言)是對我最大的鼓勵,

各位技術朋友們,我是《RocketMQ技術內幕》一書作者,CSDN2020博客之星TOP2,熱衷于中間件領域的技術分享,維護「中間件興趣圈」公眾號,旨在成體系剖析Java主流中間件,構建完備的分布式架構體系,歡迎大家大家關注我,第一時間獲得最新干貨文章,
在這里插入圖片描述

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/300291.html

標籤:其他

上一篇:【大局觀】NO.3 你真的了解“政策”嗎?

下一篇:C語言習題積累(正在更新)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more