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

Java多線程之悲觀鎖與樂觀鎖

 更新時間:2022年03月22日 10:31:40   作者:小女養(yǎng)成記  
這篇文章主要為大家詳細介紹了Java悲觀鎖與樂觀鎖,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助

問題:

1、樂觀鎖和悲觀鎖的理解及如何實現(xiàn),有哪些實現(xiàn)方式?

2、什么是樂觀鎖和悲觀鎖?

3、樂觀鎖可以重入嗎?

1. 悲觀鎖存在的問題

獨占鎖其實就是一種悲觀鎖,java的synchronized是悲觀鎖。悲觀鎖可以確保無論哪個線程持有鎖,都能獨占式訪問臨界區(qū)。雖然悲觀鎖的邏輯非常簡單,但是存在不少問題。

悲觀鎖總是假設(shè)會發(fā)生最壞的情況,每次線程讀取數(shù)據(jù)時,也會上鎖。這樣其他線程在讀取數(shù)據(jù)時就會被阻塞,直到它拿到鎖。傳統(tǒng)的關(guān)系型數(shù)據(jù)庫用到了很多悲觀鎖,比如行鎖、表鎖、讀鎖、寫鎖等。

悲觀鎖機制存在以下問題:

(1)在多線程競爭下,加鎖、釋放鎖會導致比較多的上下文切換和調(diào)度延時,引起性能問題。

(2)一個線程持有鎖后,會導致其他所有搶占此鎖的線程掛起。

(3)如果一個優(yōu)先級高的線程等待一個優(yōu)先級低的線程釋放鎖,就會導致線程的優(yōu)先級倒置,從而引發(fā)性能風險。

解決以上悲觀鎖的這些問題的有效方式是使用樂觀鎖去替代悲觀鎖。與之類似,數(shù)據(jù)庫操作中的帶版本號數(shù)據(jù)更新、JUC包的原子類,都使用了樂觀鎖的方式提升性能。

2. 通過CAS實現(xiàn)樂觀鎖

樂觀鎖的操作主要就是兩個步驟:(1)第一步:沖突檢測。(2)第二步:數(shù)據(jù)更新。

樂觀鎖一種比較典型的就是CAS原子操作,JUC強大的高并發(fā)性能是建立在CAS原子之上的。CAS操作中包含三個操作數(shù):需要操作的內(nèi)存位置(V)、進行比較的預期原值(A)和擬寫入的新值(B)。如果內(nèi)存位置V的值與預期原值A(chǔ)相匹配,那么處理器會自動將該位置的值更新為新值B;否則處理器不做任何操作。

CAS操作可以非常清晰地分為兩個步驟:

(1)檢測位置V的值是否為A。

(2)如果是,就將位置V更新為B值;否則不要更改該位置。

CAS操作的兩個步驟其實與樂觀鎖操作的兩個步驟是一致的,都是在沖突檢測后進行數(shù)據(jù)更新。

樂觀鎖是一種思想,而CAS是這種思想的一種實現(xiàn)。實際上,如果需要完成數(shù)據(jù)的最終更新,僅僅進行一次CAS操作是不夠的,一般情況下,需要進行自旋操作,即不斷地循環(huán)重試CAS操作直到成功,這也叫CAS自旋。通過CAS自旋,在不使用鎖的情況下實現(xiàn)多線程之間的變量同步,也就是說,在沒有線程被阻塞的情況下實現(xiàn)變量的同步,這叫作“非阻塞同步”,或者說“無鎖同步”。使用基于CAS自旋的樂觀鎖進行同步控制,屬于無鎖編程的一種實踐。

3. 不可重入的自旋鎖

自旋鎖的基本含義為:當一個線程在獲取鎖的時候,如果鎖已經(jīng)被其他線程獲取,調(diào)用者就一直在那里循環(huán)檢查該鎖是否已經(jīng)被釋放,一直到獲取到鎖才會退出循環(huán)。

CAS自旋鎖的實現(xiàn)原理為:

搶鎖線程不斷進行CAS自旋操作去更新鎖的owner(擁有者),如果更新成功,就表明已經(jīng)搶鎖成功,退出搶鎖方法。如果鎖已經(jīng)被其他線程獲?。ㄒ簿褪莖wner為其他線程),調(diào)用者就一直在那里循環(huán)進行owner的CAS更新操作,一直到成功才會退出循環(huán)。

public class SpinLock implements Lock {
     // 當前鎖的擁有者
    private AtomicReference<Thread> owner = new AtomicReference<>();
     @Override
    public void lock() {
        Thread t = Thread.currentThread();
        // 自旋
        while (owner.compareAndSet(null,t)){
            // 讓出CPU的時間片
            Thread.yield();
        }
    }
     @Override
    public void unlock() {
        Thread t = Thread.currentThread();
        // 只有擁有者才能獲取鎖
        if(t==owner.get()){
            // 設(shè)置owner為空,這里不需要使用compareAndSet,因為已經(jīng)通過owner做過線程檢查
            owner.set(null);
        }
    }
     // 省略其他代碼...
}

上述SpinLock是不支持重入的,即當一個線程第一次已經(jīng)獲取到了該鎖,在鎖沒有被釋放之前,如果又一次重新獲取該鎖,第二次將不能成功獲取到,因為自旋后CAS會失敗。

4. 可重入的自旋鎖

為了實現(xiàn)可重入鎖,這里引入一個計數(shù)器,用來記錄一個線程獲取鎖的次數(shù)。一個簡單的可重入的自旋鎖的代碼大致如下:

public class ReentrantSpinLock implements Lock {
     // 當前鎖的擁有者,使用Thread作為同步狀態(tài)
    AtomicReference<Thread> owner = new AtomicReference<>();
     // 記錄一個線程重復獲取鎖的次數(shù)
    private int count = 0;
     // 搶占鎖
    @Override
    public void lock() {
        Thread t =  Thread.currentThread();
        // 如果時沖入,增加重入次數(shù)后,返回
        if(t==owner.get()){
            count++;
            return;
        }
        // 自旋
        while (owner.compareAndSet(null,t)){
            Thread.yield();
        }
    }
     @Override
    public void unlock() {
        Thread t = Thread.currentThread();
        // 只有擁有者才能釋放鎖
        if(t==owner.get()){
            // 如果重入次數(shù)大于0,減少重入次數(shù)后返回
            if(count>0){
                count--;
            }else{
                // 設(shè)置擁有者為null
                owner.set(null);
            }
        }
    }
     // 省略其他代碼...
}

自旋鎖的特點:線程獲取鎖的時候,如果鎖被其他線程持有,當前線程將循環(huán)等待,直到獲取到鎖。線程搶鎖期間狀態(tài)不會改變,一直是運行狀態(tài)(RUNNABLE),在操作系統(tǒng)層面線程處于用戶態(tài)。

自旋鎖的問題:在爭用激烈的場景下,如果某個線程持有鎖的時間太長,就會導致其他空自旋的線程耗盡CPU資源。另外,如果大量的線程進行空自旋,還可能導致硬件層面的“總線風暴”。

在爭用激烈的場景下,Java輕量級鎖會快速膨脹為重量級鎖,其本質(zhì)上一是為了減少CAS空自旋,二是為了避免同一時間大量CAS操作所導致的總線風暴。那么,JUC基于CAS實現(xiàn)的輕量級鎖如何避免總線風暴呢?答案是:使用隊列對搶鎖線性進行排隊,最大程度上減少了CAS操作數(shù)量。

總結(jié)

本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!   

相關(guān)文章

  • 一文搞懂SpringBoot如何利用@Async實現(xiàn)異步調(diào)用

    一文搞懂SpringBoot如何利用@Async實現(xiàn)異步調(diào)用

    異步調(diào)用幾乎是處理高并發(fā),解決性能問題常用的手段,如何開啟異步調(diào)用?SpringBoot中提供了非常簡單的方式,就是一個注解@Async。今天我們重新認識一下@Async,以及注意事項
    2022-09-09
  • Java?泛型考古?泛型擦除?包裝類詳細解析

    Java?泛型考古?泛型擦除?包裝類詳細解析

    泛型是在Java?SE?1.5引入的的新特性,本質(zhì)是參數(shù)化類型,也就是說所操作的數(shù)據(jù)類型被指定為一個參數(shù)。這種參數(shù)類型可以用在類、接口和方法的創(chuàng)建中,分別稱為泛型類、泛型接口、泛型方法,本篇我們一起來學習泛型考古、泛型擦除、包裝類
    2022-03-03
  • 使用Java橋接模式打破繼承束縛優(yōu)雅實現(xiàn)多維度變化

    使用Java橋接模式打破繼承束縛優(yōu)雅實現(xiàn)多維度變化

    這篇文章主要為大家介紹了使用Java橋接模式打破繼承束縛,優(yōu)雅實現(xiàn)多維度變化,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-05-05
  • Java字節(jié)與字符流永久存儲json數(shù)據(jù)

    Java字節(jié)與字符流永久存儲json數(shù)據(jù)

    本篇文章給大家詳細講述了Java字節(jié)與字符流永久存儲json數(shù)據(jù)的方法,以及代碼分享,有興趣的參考學習下。
    2018-02-02
  • Spring Boot定時任務單線程多線程實現(xiàn)代碼解析

    Spring Boot定時任務單線程多線程實現(xiàn)代碼解析

    這篇文章主要介紹了Spring Boot定時任務單線程多線程實現(xiàn)代碼解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-08-08
  • uploadify java實現(xiàn)多文件上傳和預覽

    uploadify java實現(xiàn)多文件上傳和預覽

    這篇文章主要為大家詳細介紹了java結(jié)合uploadify實現(xiàn)多文件上傳和預覽的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • Java源碼解析阻塞隊列ArrayBlockingQueue介紹

    Java源碼解析阻塞隊列ArrayBlockingQueue介紹

    今天小編就為大家分享一篇關(guān)于Java源碼解析阻塞隊列ArrayBlockingQueue介紹,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-01-01
  • java 讀取zip文件的兩種方式示例詳解

    java 讀取zip文件的兩種方式示例詳解

    ZIP(壓縮文件)是一種常見的文件格式,在Java中可以使用java.util.zip包提供的API來讀取和處理ZIP文件,本文將介紹如何使用Java讀取ZIP文件,并提供代碼示例,感興趣的朋友跟隨小編一起看看吧
    2024-07-07
  • 通過jenkins發(fā)布java項目到目標主機上的詳細步驟

    通過jenkins發(fā)布java項目到目標主機上的詳細步驟

    這篇文章主要介紹了通過jenkins發(fā)布java項目到目標主機上的詳細步驟,發(fā)布java項目的步驟很簡單,通過拉取代碼并打包,備份目標服務器上已有的要發(fā)布項目,具體內(nèi)容詳情跟隨小編一起看看吧
    2021-10-10
  • Java從JDK源碼角度對Object進行實例分析

    Java從JDK源碼角度對Object進行實例分析

    這篇文章主要介紹了Java從JDK源碼角度對Object進行實例分析,具有一定借鑒價值,需要的朋友可以參考下。
    2017-12-12

最新評論