最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中的CAS無(wú)鎖機(jī)制實(shí)現(xiàn)原理詳解

 更新時(shí)間:2024年01月13日 10:10:07   作者:java架構(gòu)師-太陽(yáng)  
這篇文章主要介紹了Java中的CAS無(wú)鎖機(jī)制實(shí)現(xiàn)原理詳解,無(wú)鎖機(jī)制,是樂(lè)觀鎖的一種實(shí)現(xiàn),并發(fā)情況下保證對(duì)共享變量值更改的原子性,CAS是Java中Unsafe類里面的方法,底層通過(guò)調(diào)用C語(yǔ)言接口,再通過(guò)cup硬件指令保證原子性,需要的朋友可以參考下

CAS(Compare And Swap) 比較和替換

無(wú)鎖機(jī)制,是樂(lè)觀鎖的一種實(shí)現(xiàn)

并發(fā)情況下保證對(duì)共享變量值更改的原子性 CAS是Java中Unsafe類里面的方法 底層通過(guò)調(diào)用C語(yǔ)言接口,再通過(guò)cup硬件指令保證原子性

實(shí)現(xiàn)算法

三個(gè)參數(shù)(V,E,N): V是要更新的變量,E是預(yù)期值,N是新值。當(dāng)V的值等于E值時(shí),將V的值設(shè)為N,若V值和E值不同,則說(shuō)明已經(jīng)有其他線程做了更新,當(dāng)前線程方式自旋重來(lái)。

最后,CAS返回當(dāng)前V的真實(shí)值,CAS 一定要volatile變量配合,這樣才能保證每次拿到的變量是主內(nèi)存中最新的那個(gè)值

使用樂(lè)觀鎖思想,多個(gè)線程用CAS操作一個(gè)變量時(shí),只有一個(gè)成功且成功更新。

失敗的線程不會(huì)被掛起,僅是被告知失敗,允許再次嘗試,也允許失敗線程放棄操作?;谶@樣的原理,CAS操作即使沒(méi)有鎖,也可以發(fā)現(xiàn)其他線程對(duì)當(dāng)前線程的干擾,并進(jìn)行恰當(dāng)?shù)奶幚?/p>

與鎖相比,使用CAS會(huì)使程序看起來(lái)更加復(fù)雜一些,但由于其非阻塞性,對(duì)死鎖天生免疫,且線程間的相互影響也遠(yuǎn)遠(yuǎn)比基于鎖的方式要小,無(wú)鎖方式?jīng)]有鎖競(jìng)爭(zhēng),也沒(méi)有線程間頻繁調(diào)度的開(kāi)銷。

因此,比基于鎖的方式擁有更優(yōu)越的性能

底層實(shí)現(xiàn)

通過(guò)硬件保證了比較更新的原子性和可見(jiàn)性,實(shí)現(xiàn)方式是基于硬件平臺(tái)的匯編指令,大部分的現(xiàn)代處理器都已經(jīng)支持原子化的CAS指令,在JDK 5.0以后,虛擬機(jī)便可以使用這個(gè)指令來(lái)實(shí)現(xiàn)并發(fā)操作和并發(fā)數(shù)據(jù)結(jié)構(gòu),CAS是cup的原子指令(cmpxchg),不會(huì)造成數(shù)據(jù)不一致,執(zhí)行cmpxchg指令時(shí),會(huì)判斷當(dāng)前系統(tǒng)是否是多核系統(tǒng),如果是就給總線加鎖,只會(huì)有一個(gè)線程給總線加鎖成功,加速成功之后會(huì)執(zhí)行cas操作,也就是cas的原子性實(shí)際上是cup實(shí)現(xiàn)的,比起synchronized,排他時(shí)間很短,多線程下性能高

優(yōu)點(diǎn)

(1) 高并發(fā)的情況下,比有鎖的程序擁有更好的性能,是輕量級(jí)鎖

(2) 它天生就是死鎖免疫的

(3) 線程不會(huì)阻塞(線程阻塞到喚醒運(yùn)行成本較高),一直處于用戶態(tài)

缺點(diǎn)

(1) 若一直獲取不到鎖死循環(huán),消耗cup資源,可能導(dǎo)致cup飆高(需要控制次數(shù))   

(2) ABA問(wèn)題:若內(nèi)存地址V初次讀取的值是A,在CAS等待期間它的值曾經(jīng)被改成了B,后來(lái)又被改回為A,那CAS操作就會(huì)誤認(rèn)為它從來(lái)沒(méi)有被改變過(guò)。解決辦法,加版本號(hào)屬性,每次值變化了給版本號(hào)加1,。ABA的問(wèn)題對(duì)結(jié)果沒(méi)有影響,只會(huì)和CAS概念沖突了

(3) 不能保證代碼塊的原子性:CAS只保證一個(gè)變量的原子性,若更新多變量同時(shí)原子性要用synchronized

適用場(chǎng)景

CAS 適合簡(jiǎn)單對(duì)象的操作,比如布爾值、整型值等;

典型的使用場(chǎng)景有兩個(gè)

 (1)   J.U.C里面Atomic的原子實(shí)現(xiàn),比如AtomicInteger,AtomicLong。

 (2)   實(shí)現(xiàn)多線程對(duì)共享資源競(jìng)爭(zhēng)的互斥性質(zhì),比如在AQS/ConcurrentHashMap/ConcurrentLinkedQueue等

通常和自旋鎖同時(shí)使用

代碼示例

有一個(gè)成員變量state,默認(rèn)值是0, 定義了一個(gè)方法doSomething(),判斷state是否為0 ,如果為0,就修改成1。

這個(gè)邏輯在多線程環(huán)境下,會(huì)存在原子性的問(wèn)題,因?yàn)檫@里是一個(gè)典型的,Read - Write的操作。

一般會(huì)在doSomething()這個(gè)方法上加同步鎖來(lái)解決原子性問(wèn)題, 但加同步鎖,會(huì)帶來(lái)性能上的損耗,

public class Example {
    private int state = 0;
    public void doSomething() {
        if (state == 0) {
            state = 1;
        }
    }
}

使用CAS機(jī)制來(lái)進(jìn)行優(yōu)化

調(diào)用Unsafe類的objectFieldOffset()傳入類的變量得到變量在內(nèi)存中的偏移量

調(diào)用Unsafe類的compareAndSwapInt()方法進(jìn)行更新,傳入四個(gè)參數(shù):當(dāng)前對(duì)象實(shí)例/變量在內(nèi)存地址中的偏移量/預(yù)期值/更新值

比較變量?jī)?nèi)存地址偏移量對(duì)應(yīng)的值和傳入的預(yù)期值是否相等,若相等,修改內(nèi)存地址中變量的值為要更新的值,否則,返回false

compareAndSwap()是native方法,底層實(shí)現(xiàn)中,在多核CPU環(huán)境下,會(huì)增加一個(gè)Lock指令對(duì)緩存或總線加鎖,從而保證比較并替換這兩個(gè)指令的原子性。

這里注意變量要加volatile

public class Example {
    
    private volatile int state = 0;
    private static final Unsafe UNSAFE = Unsafe.getUnsafe();
    private static final long stateOffset;

    static {
        try {
            stateOffset = UNSAFE.objectFieldOffset(Example.class.getDeclaredField("state"));
        } catch (Exception ex){
            throw new Error(ex);
        }
    }

    public void doSomething() {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, 0 ,1)) {
            // TODO
        }
    }

    public int getState() {
        return state;
    }
}
public static void main(String[] args) {
    Example example = new Example();
    example.doSomething();
    System.out.println(example.getState());
}

測(cè)試的時(shí)候會(huì)報(bào)錯(cuò)會(huì)在這行代碼 Unsafe.getUnsafe();

Caused by: java.lang.SecurityException: Unsafe
    at sun.misc.Unsafe.getUnsafe(Unsafe.java:90)
    at com.test.Example.<clinit>(Example.java:7)
    ... 1 more

查看Unsafe.getUnsafe()源碼

@CallerSensitive
public static Unsafe getUnsafe() {
    Class var0 = Reflection.getCallerClass();
    if (!VM.isSystemDomainLoader(var0.getClassLoader())) {
        throw new SecurityException("Unsafe");
    } else {
        return theUnsafe;
    }
}

如果不是systemClassLoader則會(huì)拋出SecurityException(“Unsafe”)異常,所以用戶編寫(xiě)的程序使用不了unsafe實(shí)例。

怎么解決ABA問(wèn)題,加一個(gè)版本號(hào) 單線程情況下

public static void main(String[] args) {

    Book javaBook = new Book(1,"javaBook");

    // 參數(shù)1是版本號(hào)
    AtomicStampedReference<Book> stampedReference = new AtomicStampedReference<>(javaBook,1);

    System.out.println(stampedReference.getReference() + "\t" + stampedReference.getStamp());

    Book mysqlBook = new Book(2,"mysqlBook");

    boolean b;
    // 在判斷的時(shí)候不斷要判斷book,還要判斷版本號(hào),更新的時(shí)候更新book的值,也要更新版本號(hào)(這里是加1)
    b = stampedReference.compareAndSet(javaBook, mysqlBook, stampedReference.getStamp(), stampedReference.getStamp() + 1);

    System.out.println(b + "\t" + stampedReference.getReference() + "\t" + stampedReference.getStamp());

    b = stampedReference.compareAndSet(mysqlBook, javaBook, stampedReference.getStamp(), stampedReference.getStamp() + 1);

    System.out.println(b+"\t"+stampedReference.getReference()+"\t"+stampedReference.getStamp());

}

對(duì)線程情亂下

public class ABADemo {
    static AtomicStampedReference<Integer> stampedReference = new AtomicStampedReference<>(100,1);
    public static void main(String[] args) {
        new Thread(() -> {
            int stamp = stampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "\t" + "首次版本號(hào):" + stamp);
            // 暫停500毫秒,保證后面的t4線程初始化拿到的版本號(hào)和我一樣
            try { TimeUnit.MILLISECONDS.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); }
            // 從100改到101
            stampedReference.compareAndSet(100,101,stampedReference.getStamp(),stampedReference.getStamp()+1);
            System.out.println(Thread.currentThread().getName()+"\t"+"2次流水號(hào):"+stampedReference.getStamp());
            // 從101改到100
            stampedReference.compareAndSet(101,100,stampedReference.getStamp(),stampedReference.getStamp()+1);
            System.out.println(Thread.currentThread().getName()+"\t"+"3次流水號(hào):"+stampedReference.getStamp());
        },"t3").start();
        new Thread(() -> {
            int stamp = stampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+ "\t" + "首次版本號(hào):" + stamp);
            // 暫停1秒鐘線程,等待上面的t3線程,發(fā)生了ABA問(wèn)題
            try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
            boolean b = stampedReference.compareAndSet(100, 2022, stamp, stamp + 1);
            System.out.println(b+"\t"+stampedReference.getReference()+"\t"+stampedReference.getStamp());
        },"t4").start();
    }
}

到此這篇關(guān)于Java中的CAS無(wú)鎖機(jī)制實(shí)現(xiàn)原理詳解的文章就介紹到這了,更多相關(guān)CAS無(wú)鎖機(jī)制原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • microlog4android將Android Log日志寫(xiě)到SD卡文件中實(shí)現(xiàn)方法

    microlog4android將Android Log日志寫(xiě)到SD卡文件中實(shí)現(xiàn)方法

    這篇文章主要介紹了microlog4android將Android Log日志寫(xiě)到SD卡文件中實(shí)現(xiàn)方法的相關(guān)資料,需要的朋友可以參考下
    2016-10-10
  • SpringBoot中的@EnableConfigurationProperties注解詳細(xì)解析

    SpringBoot中的@EnableConfigurationProperties注解詳細(xì)解析

    這篇文章主要介紹了SpringBoot中的@EnableConfigurationProperties注解詳細(xì)解析,如果一個(gè)配置類只配置@ConfigurationProperties注解,而沒(méi)有使用@Component或者實(shí)現(xiàn)了@Component的其他注解,那么在IOC容器中是獲取不到properties 配置文件轉(zhuǎn)化的bean,需要的朋友可以參考下
    2024-01-01
  • Java的函數(shù)式編程詳解

    Java的函數(shù)式編程詳解

    用了這么久的Java8,我尋思這種話也好意思說(shuō)出來(lái)嗎,難道自己是PythonBoy出身就是看不懂Java的理由嗎,身為一個(gè)合格的后端Boy不會(huì)還有人看不明白Java的函數(shù)式編程吧,接下來(lái)小編和大家淺聊一下Java的函數(shù)式編程,需要的朋友可以參考下
    2023-10-10
  • java面向?qū)ο缶幊讨匾拍罾^承和多態(tài)示例解析

    java面向?qū)ο缶幊讨匾拍罾^承和多態(tài)示例解析

    這篇文章主要為大家介紹了java面向?qū)ο缶幊痰膬蓚€(gè)重要概念繼承和多態(tài)示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-05-05
  • Java根據(jù)表達(dá)式獲取對(duì)象中的值及設(shè)置值的例子

    Java根據(jù)表達(dá)式獲取對(duì)象中的值及設(shè)置值的例子

    這篇文章主要介紹了Java根據(jù)表達(dá)式獲取對(duì)象中的值及設(shè)置值的例子,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2025-03-03
  • 詳解springboot WebTestClient的使用

    詳解springboot WebTestClient的使用

    WebClient是一個(gè)響應(yīng)式客戶端,它提供了RestTemplate的替代方法。這篇文章主要介紹了詳解springboot WebTestClient的使用, 具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-11-11
  • Java doGet, doPost方法和文件上傳實(shí)例代碼

    Java doGet, doPost方法和文件上傳實(shí)例代碼

    這篇文章主要介紹了Java doGet, doPost方法和文件上傳實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下
    2016-11-11
  • 三道java新手入門(mén)面試題,通往自由的道路--JVM

    三道java新手入門(mén)面試題,通往自由的道路--JVM

    這篇文章主要為大家分享了最有價(jià)值的3道JVM面試題,涵蓋內(nèi)容全面,包括數(shù)據(jù)結(jié)構(gòu)和算法相關(guān)的題目、經(jīng)典面試編程題等,對(duì)hashCode方法的設(shè)計(jì)、垃圾收集的堆和代進(jìn)行剖析,感興趣的小伙伴們可以參考一下
    2021-07-07
  • 使用Spring開(kāi)啟@Async異步方式(javaconfig配置)

    使用Spring開(kāi)啟@Async異步方式(javaconfig配置)

    這篇文章主要介紹了使用Spring開(kāi)啟@Async異步方式(javaconfig配置),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • Spring?Boot全局異常處理實(shí)戰(zhàn)指南

    Spring?Boot全局異常處理實(shí)戰(zhàn)指南

    在開(kāi)發(fā)過(guò)程中,異常是不可避免的,Spring?Boot提供了一種簡(jiǎn)單而強(qiáng)大的機(jī)制來(lái)處理應(yīng)用程序中的異常,即全局異常處理,這篇文章主要介紹了Spring?Boot全局異常處理的相關(guān)資料,需要的朋友可以參考下
    2025-09-09

最新評(píng)論

翁牛特旗| 富锦市| 屯昌县| 平塘县| 如皋市| 西充县| 五河县| 内江市| 汉中市| 丰宁| 固阳县| 绥芬河市| 寻乌县| 盘山县| 苏尼特右旗| 大同市| 海盐县| 靖江市| 张家界市| 江津市| 达州市| 全州县| 永兴县| 石台县| 龙游县| 唐海县| 西乌珠穆沁旗| 定兴县| 高淳县| 手游| 林芝县| 内乡县| 滦平县| 雅江县| 北碚区| 曲阜市| 晋州市| 遂平县| 克拉玛依市| 西昌市| 江阴市|