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

Java中的CAS無鎖機制實現(xiàn)原理詳解

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

CAS(Compare And Swap) 比較和替換

無鎖機制,是樂觀鎖的一種實現(xiàn)

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

實現(xiàn)算法

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

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

使用樂觀鎖思想,多個線程用CAS操作一個變量時,只有一個成功且成功更新。

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

與鎖相比,使用CAS會使程序看起來更加復雜一些,但由于其非阻塞性,對死鎖天生免疫,且線程間的相互影響也遠遠比基于鎖的方式要小,無鎖方式?jīng)]有鎖競爭,也沒有線程間頻繁調(diào)度的開銷。

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

底層實現(xiàn)

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

優(yōu)點

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

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

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

缺點

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

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

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

適用場景

CAS 適合簡單對象的操作,比如布爾值、整型值等;

典型的使用場景有兩個

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

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

通常和自旋鎖同時使用

代碼示例

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

這個邏輯在多線程環(huán)境下,會存在原子性的問題,因為這里是一個典型的,Read - Write的操作。

一般會在doSomething()這個方法上加同步鎖來解決原子性問題, 但加同步鎖,會帶來性能上的損耗,

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

使用CAS機制來進行優(yōu)化

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

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

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

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

這里注意變量要加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());
}

測試的時候會報錯會在這行代碼 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則會拋出SecurityException(“Unsafe”)異常,所以用戶編寫的程序使用不了unsafe實例。

怎么解決ABA問題,加一個版本號 單線程情況下

public static void main(String[] args) {

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

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

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

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

    boolean b;
    // 在判斷的時候不斷要判斷book,還要判斷版本號,更新的時候更新book的值,也要更新版本號(這里是加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());

}

對線程情亂下

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" + "首次版本號:" + stamp);
            // 暫停500毫秒,保證后面的t4線程初始化拿到的版本號和我一樣
            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次流水號:"+stampedReference.getStamp());
            // 從101改到100
            stampedReference.compareAndSet(101,100,stampedReference.getStamp(),stampedReference.getStamp()+1);
            System.out.println(Thread.currentThread().getName()+"\t"+"3次流水號:"+stampedReference.getStamp());
        },"t3").start();
        new Thread(() -> {
            int stamp = stampedReference.getStamp();
            System.out.println(Thread.currentThread().getName()+ "\t" + "首次版本號:" + stamp);
            // 暫停1秒鐘線程,等待上面的t3線程,發(fā)生了ABA問題
            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();
    }
}

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

相關文章

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

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

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

    SpringBoot中的@EnableConfigurationProperties注解詳細解析

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

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

    用了這么久的Java8,我尋思這種話也好意思說出來嗎,難道自己是PythonBoy出身就是看不懂Java的理由嗎,身為一個合格的后端Boy不會還有人看不明白Java的函數(shù)式編程吧,接下來小編和大家淺聊一下Java的函數(shù)式編程,需要的朋友可以參考下
    2023-10-10
  • java面向對象編程重要概念繼承和多態(tài)示例解析

    java面向對象編程重要概念繼承和多態(tài)示例解析

    這篇文章主要為大家介紹了java面向對象編程的兩個重要概念繼承和多態(tài)示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-05-05
  • Java根據(jù)表達式獲取對象中的值及設置值的例子

    Java根據(jù)表達式獲取對象中的值及設置值的例子

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

    詳解springboot WebTestClient的使用

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

    Java doGet, doPost方法和文件上傳實例代碼

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

    三道java新手入門面試題,通往自由的道路--JVM

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

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

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

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

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

最新評論

镶黄旗| 泰兴市| 额济纳旗| 泸水县| 如东县| 黔东| 禹州市| 泽普县| 佛冈县| 陵水| 淅川县| 安泽县| 旬邑县| 乌审旗| 含山县| 绩溪县| 灵丘县| 景泰县| 峨眉山市| 卢龙县| 双峰县| 海伦市| 寿光市| 阳原县| 宁南县| 元阳县| 宾川县| 怀仁县| 濮阳县| 定日县| 丹寨县| 开封县| 土默特左旗| 永寿县| 乌兰察布市| 桐梓县| 平原县| 漳平市| 江油市| 无极县| 阿拉善右旗|