欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java常見的鎖策略圖文詳解(附實(shí)例代碼)

 更新時(shí)間:2025年10月23日 09:41:20   作者:Jul1en_  
Java中的鎖方法是指通過特定的機(jī)制來確保多線程環(huán)境下對(duì)共享資源的互斥訪問,以避免數(shù)據(jù)不一致和競態(tài)條件,這篇文章主要介紹了Java常見鎖策略的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下

常見的鎖策略

樂觀鎖 & 悲觀鎖

加鎖的時(shí)候預(yù)測這個(gè)鎖出現(xiàn)競爭性的可能性大還是???

預(yù)測這個(gè)鎖出現(xiàn)競爭可能性小 —— 樂觀鎖

預(yù)測這個(gè)鎖出現(xiàn)競爭可能性大 —— 悲觀鎖

輕量級(jí)鎖 & 重量級(jí)鎖

加鎖的開銷小 —— 輕量級(jí)鎖

加鎖的開銷大 —— 重量級(jí)鎖

自旋鎖 & 掛起等待鎖

遇到鎖沖突,先不著急阻塞,而是嘗試重新得到鎖。這個(gè)操作不涉及內(nèi)核態(tài),僅在用戶態(tài)方面進(jìn)行,所以更加輕量 —— 自旋鎖

遇到鎖沖突,阻塞等待,等到合適的時(shí)機(jī)再拿鎖。涉及到系統(tǒng)的內(nèi)部調(diào)度,開銷大 —— 掛起等待鎖

公平鎖 & 非公平鎖

JVM約定了“先來后到” 是公平鎖(先請(qǐng)求得到鎖的線程會(huì)得到鎖)

“公平競爭,各憑本事”是不公平鎖(解鎖后,多個(gè)線程同時(shí)競爭一把鎖)

可重入鎖 & 不可重入鎖

一個(gè)線程,一把鎖,同時(shí)加鎖兩次

如果沒死鎖:可重入鎖;死鎖了:不可重入鎖

C++的std :: mutex 是不可重入鎖

可重入鎖也稱“可遞歸鎖”

synchronized是可重入鎖,以第一次加鎖為真,第二次加鎖跳過

普通互斥鎖 & 讀寫鎖

普通互斥鎖有

  • 加鎖
  • 解鎖

讀寫鎖有

  • 加讀鎖
  • 加寫鎖
  • 解鎖

如果代碼中線程進(jìn)行讀的操作,那可以使用“讀鎖”,寫的操作使用“寫鎖”

讀鎖與讀鎖之間不存在互斥 —— 讀的過程數(shù)據(jù)不會(huì)發(fā)生改變,只是讀

讀鎖與寫鎖之間存在互斥 —— 讀的過程中可能會(huì)被“寫”修改

寫鎖與寫鎖之間存在互斥 —— A寫的時(shí)候肯定不能被B修改

synchronized鎖

synchronized鎖是普通互斥鎖,是可重入鎖,是非公平鎖,有著自適應(yīng)的特點(diǎn),根據(jù)內(nèi)部鎖競爭的激烈程度,自動(dòng)調(diào)整內(nèi)部策略,感知不到,干預(yù)不了,但我們需要知道內(nèi)部策略細(xì)節(jié)

鎖升級(jí)

鎖消除

針對(duì)synchronized的一種編譯器優(yōu)化,在保證邏輯不變的情況下,如果編譯器確定你寫的代碼不需要鎖,但你手動(dòng)加了鎖,會(huì)嘗試把鎖去掉。

但這個(gè)優(yōu)化十分保守,沒有十拿九穩(wěn)的情況下不會(huì)觸發(fā),我們也不能依賴編譯器優(yōu)化這個(gè)機(jī)制來寫代碼

鎖粗化

關(guān)聯(lián)到鎖的粒度,編譯器會(huì)根據(jù)實(shí)際情況來操作

如果鎖的粒度小,證明加鎖解鎖中間的邏輯少,雖然會(huì)留出時(shí)間給其他的線程來得到鎖,但也增加了線程競爭的次數(shù),也會(huì)增加阻塞的時(shí)間。

鎖的粒度雖然大,但是中間執(zhí)行的邏輯時(shí)間長,沒有反復(fù)加鎖解鎖的操作,其他線程也只能競爭一次。

CAS

Compare And Swap

解決線程安全,加鎖是一種普遍的策略,CAS是解決線程安全問題的另一種思路

顧名思義 比較與交換 —— 比較一個(gè)內(nèi)存與一個(gè)寄存器內(nèi)部的值,如果他兩的值相同,內(nèi)存會(huì)與寄存器另外一個(gè)值進(jìn)行交換,交換后更關(guān)心內(nèi)存更新之后的值,就可以實(shí)現(xiàn)“鎖”的功能

這是CAS的執(zhí)行邏輯

上述針對(duì)CAS的執(zhí)行邏輯,能看出它并不是函數(shù),而是一條CPU指令,而且是原子的,那JVM如何運(yùn)行的呢?

  1. CPU提供了CAS的執(zhí)行指令
  2. 操作系統(tǒng)對(duì)CAS指令進(jìn)行了封裝并提供了API,通過API可以調(diào)用CAS機(jī)制
  3. JVM就可以通過操作系統(tǒng)調(diào)用API
  4. 我們的代碼就可以使用CAS
  5. Java對(duì)CAS進(jìn)一步封裝,Java不建議你用CAS,但內(nèi)部像==原子類==、自旋鎖、synchronized,ConcurrentHashMap都使用了CAS

這又引申出了**原子類**

原子類

內(nèi)部沒有加鎖,但基于CAS實(shí)現(xiàn)了,所以不加鎖也能保證線程安全

可以實(shí)例化原子類的整數(shù)/字符…,并設(shè)定初始化的值

方法與注釋中的操作是一一對(duì)應(yīng)的,舉例getAndIncrement 是先得到舊值,再Increment(+1)

自旋鎖

synchronized內(nèi)部的自旋鎖,就是基于CAS實(shí)現(xiàn),這就是為什么自旋鎖輕量的原因(“無鎖”)

ABA問題

CAS的循環(huán)檢測,判定是否有其他線程穿插修改執(zhí)行的依據(jù)是循環(huán)判定是檢測的值是否有變化,但是這種檢測方式是不嚴(yán)謹(jǐn)?shù)?,沒有變化不能代表沒有被修改過,可能某時(shí)刻的值是A,在下一時(shí)刻被修改了B,然后在第二次檢測之前又被修改回了A,這就是ABA問題

大部分時(shí)候ABA問題不會(huì)引起bug,但在某些極端的情況下是會(huì)的,就像買新手機(jī)一樣,你買的手機(jī)是“翻新機(jī)”,但翻新機(jī)不代表一定就有問題,只是說翻新機(jī)出現(xiàn)問題的概率比全新機(jī)更大的~

ABA的核心是:
在通過A的值相同判定是否有插隊(duì),由于次數(shù)的修改有加也有減,就可能會(huì)出現(xiàn)ABA的問題,那么有一些解決方案:限制此處的操作(只能加或者只能減)、或者引入“版本號(hào)”概念,每次修改都讓“版本號(hào)”+1,再次使用判定的時(shí)候就不是用值來判定了,而是看版本號(hào)

那我們可以引申出以下問題??

  1. CAS是什么?
  2. CAS有什么應(yīng)用場景(CAS如何實(shí)現(xiàn)鎖)?
  3. CAS的ABA問題是怎么樣的

JUC包中常見的類

JUC——Java.util.Concurrent

Callable接口

是泛型接口,描述一個(gè)任務(wù),唯一方法call()?

  • 可以返回一個(gè)結(jié)果
  • 可以拋出受檢異常

類似與Runnable,但與Runnable的void run() 不同的是Callable的 V call() throws Exception 有返回值,通常Callable任務(wù)會(huì)被交給FutureTaskExecutorService.submit()來執(zhí)行

		Callable<Integer> callable = new Callable<Integer>() {
     		int sum = 0;
            @Override
            public Integer call() throws Exception {
                for (int i = 1; i <= 1000; i++) {
                    sum += i;
                }
                return sum;
            }
        };
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        Thread t = new Thread(futureTask);

FutureTask & Callable 的聯(lián)系

  1. Callable是一個(gè)接口,類似與Runnable,但有返回值,并且能拋出受檢異常

    • 使用場景:需要執(zhí)行一個(gè)有返回值的任務(wù)時(shí)
  2. FutureTask是一個(gè)類

    • 實(shí)現(xiàn)了RunnableFuture 接口

      ?public class FutureTask<V> implements RunnableFuture<V>?

    • 而RunnableFuture 又繼承了Runnable 和 Future

      ?public interface RunnableFuture<V> extends Runnable, Future<V> { void run(); }?

    • 所以FutureTask既是一個(gè)任務(wù)Runnable,也是一個(gè)容器Future?

    • 它內(nèi)部持有Callable,并在run()方法中調(diào)用Callable的call()方法

他們的call()/ run()和線程的run()關(guān)系
  1. ?Callable.call()?

    • 是用戶定義的邏輯,可以返回,可以拋出受檢異常
    • 不能直接交給Thread運(yùn)行,因?yàn)樗皇?code>Runnable,Thread只能運(yùn)行Runnable?
  2. ?FutureTask.run()?

    • Runnable的實(shí)現(xiàn)
    • 內(nèi)部會(huì)調(diào)用Callable.call(),并把結(jié)果存起來,通過.get()得到
  3. ?Thread.run()?

    • Thread本身的run()是空的,當(dāng)你傳入一個(gè)Runnable后它才會(huì)調(diào)用Runnable.run()?
    • 如果傳入的是FutureTask,則會(huì)調(diào)用FutureTask.run(),再通過FutureTask.run()來調(diào)用Callable.call()?

執(zhí)行鏈條:

  1. ?Thread.run() -> Runnable.run()?
  2. ?Thread.run() -> FutureTask.run() -> Callable.call()?

ExecutorService & Callable

?ExecutorService.submit(Callable)不會(huì)直接調(diào)用Callable.call(),而是間接調(diào)用

  1. newTaskFor(task)

    • 會(huì)把 Callable 包裝成一個(gè) FutureTaskRunnableFuture 的實(shí)現(xiàn)類)。
    • 這樣它既是一個(gè) Runnable(能被線程池執(zhí)行),又是一個(gè) Future(能保存結(jié)果)。
  2. execute(ftask)

    • 線程池把 FutureTask 當(dāng)作 Runnable,交給工作線程運(yùn)行。
    • 線程執(zhí)行時(shí),會(huì)調(diào)用 FutureTask.run()
  3. FutureTask.run()

    • 內(nèi)部調(diào)用 callable.call() 來真正執(zhí)行你的邏輯,并把結(jié)果存起來。

執(zhí)行鏈條:

ExecutorService.submit(Callable)

  1. -> newTaskFor(Callable) (Callable -> FutureTask)
  2. -> execute(FutureTask)
  3. -> Worker(Thread) 執(zhí)行
  4. -> FutureTask.run()
  5. -> Callable.call()

對(duì)比Runnable,用Callable的優(yōu)勢

  1. 有返回值

    • ?Runnable.run()沒有返回值
    • ?Callable.call()有返回值,結(jié)合Future/FutureTask使用,就能拿到異步計(jì)算的結(jié)果

    優(yōu)點(diǎn):適合計(jì)算類任務(wù),例如“統(tǒng)計(jì)一批文件的大小”“并行計(jì)算求和”等

  2. 可以拋出受檢異常

    • Runnable不能拋出受檢異常,有異常只能自己捕獲或者包裝成RuntimeException
    • Callable可以拋出受檢異常,并保存到Future中,調(diào)用get()再拋出

    優(yōu)點(diǎn):讓異常處理邏輯更加自然,避免任務(wù)里硬編碼try-catch

  3. 和并發(fā)框架集成好

    • ExecutorService.submit(Callable task)會(huì)返回一個(gè)Future,可以用來

      • 獲取結(jié)果:future.get()?
      • 取消任務(wù):future.cancel(true)?
      • 檢查狀態(tài):future.isDone()?

    優(yōu)點(diǎn):和Runnable相比,更合適在生產(chǎn)級(jí)并發(fā)框架應(yīng)用(線程池,F(xiàn)orkJoinPool)

  4. 支持函數(shù)式編程(lambda)

    • Callable是函數(shù)式接口,支持lambda表達(dá)式

      ?Callable<Integer> task = () -> 42;?

    優(yōu)點(diǎn):代碼更加簡潔美觀

ReentrantLock可重入鎖

一個(gè)可重入互斥lock具有與使用synchronized方法和語句訪問的隱式監(jiān)視鎖相同的基本行為和語義,但具有擴(kuò)展功能。

在上古時(shí)期的時(shí)候還沒有synchronized,當(dāng)時(shí)java的加鎖就是用的ReentrantLock

  1. 支持trylock

    嘗試加鎖,如果加鎖失敗,就直接返回(放棄),也支持指定超時(shí)時(shí)間

  2. 支持公平鎖

  3. 等待通知機(jī)制

    • synchronized.wait()搭配notify(),只支持隨機(jī)喚醒或喚醒全部
    • ReentrantLock搭配Condition類,喚醒功能更加豐富,能指定喚醒

與synchronized區(qū)別

相同點(diǎn):

  1. synchronized 和 ReentrantLock 都是 Java 中提供的可重入鎖

不同點(diǎn):

  1. 用法不同:synchronized 可以用來修飾普通方法、靜態(tài)方法和代碼塊;ReentrantLock 只能用于代碼塊;
  2. 獲取和釋放鎖的機(jī)制不同:進(jìn)入synchronized 塊自動(dòng)加鎖和執(zhí)行完后自動(dòng)釋放鎖; ReentrantLock 需要顯示的手動(dòng)加鎖和釋放鎖;
  3. 鎖類型不同:synchronized 是非公平鎖; ReentrantLock 默認(rèn)為非公平鎖,也可以手動(dòng)指定為公平鎖;
  4. 響應(yīng)中斷不同:synchronized 不能響應(yīng)中斷;ReentrantLock 可以響應(yīng)中斷,可用于解決死鎖的問題;
  5. 底層實(shí)現(xiàn)不同:synchronized 是 JVM 層面通過監(jiān)視器實(shí)現(xiàn)的;ReentrantLock 是基于 AQS 實(shí)現(xiàn)的。

‍總結(jié)

到此這篇關(guān)于Java常見鎖策略的文章就介紹到這了,更多相關(guān)Java常見鎖策略內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot實(shí)現(xiàn)緩存組件配置動(dòng)態(tài)切換的步驟詳解

    SpringBoot實(shí)現(xiàn)緩存組件配置動(dòng)態(tài)切換的步驟詳解

    現(xiàn)在有多個(gè)springboot項(xiàng)目,但是不同的項(xiàng)目中使用的緩存組件是不一樣的,有的項(xiàng)目使用redis,有的項(xiàng)目使用ctgcache,現(xiàn)在需要用同一套代碼通過配置開關(guān),在不同的項(xiàng)目中切換這兩種緩存,本文介紹了SpringBoot實(shí)現(xiàn)緩存組件配置動(dòng)態(tài)切換的步驟,需要的朋友可以參考下
    2024-07-07
  • SpringBoot?整合Security權(quán)限控制的初步配置

    SpringBoot?整合Security權(quán)限控制的初步配置

    這篇文章主要為大家介紹了SpringBoot?整合Security權(quán)限控制的初步配置實(shí)例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • 詳解SpringBoot2 使用Spring Session集群

    詳解SpringBoot2 使用Spring Session集群

    這篇文章主要介紹了SpringBoot2 使用Spring Session集群,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下
    2019-04-04
  • Java建造者設(shè)計(jì)模式詳解

    Java建造者設(shè)計(jì)模式詳解

    這篇文章主要為大家詳細(xì)介紹了Java建造者設(shè)計(jì)模式,對(duì)建造者設(shè)計(jì)模式進(jìn)行分析理解,感興趣的小伙伴們可以參考一下
    2016-02-02
  • java中關(guān)于移位運(yùn)算符的demo與總結(jié)(推薦)

    java中關(guān)于移位運(yùn)算符的demo與總結(jié)(推薦)

    下面小編就為大家?guī)硪黄猨ava中關(guān)于移位運(yùn)算符的demo與總結(jié)(推薦)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-05-05
  • 布隆過濾器面試如何快速判斷元素是否在集合里

    布隆過濾器面試如何快速判斷元素是否在集合里

    這篇文章主要為大家介紹了布隆過濾器面試中如何快速判斷元素是否在集合里的完美回復(fù),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步
    2022-03-03
  • VScode 打造完美java開發(fā)環(huán)境最新教程

    VScode 打造完美java開發(fā)環(huán)境最新教程

    這篇文章主要介紹了VScode 打造完美java開發(fā)環(huán)境最新教程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • Java中的抽象工廠模式_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Java中的抽象工廠模式_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    抽象工廠模式是工廠方法模式的升級(jí)版本,他用來創(chuàng)建一組相關(guān)或者相互依賴的對(duì)象。下面通過本文給大家分享Java中的抽象工廠模式,感興趣的朋友一起看看吧
    2017-08-08
  • Spring5中的WebClient使用方法詳解

    Spring5中的WebClient使用方法詳解

    這篇文章主要給大家介紹了關(guān)于Spring5中WebClient使用方法的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Spring5具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • 關(guān)于RequestMapping注解的作用說明

    關(guān)于RequestMapping注解的作用說明

    這篇文章主要介紹了關(guān)于RequestMapping注解的作用說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。
    2022-01-01

最新評(píng)論