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

Java的原子類無鎖并發(fā)利器詳解

 更新時(shí)間:2023年12月22日 09:34:21   作者:Java面試365  
這篇文章主要介紹了Java的原子類無鎖并發(fā)利器詳解,原子類同樣能夠解決互斥性問題、原子性問題除此之外,因?yàn)樵宇愂菬o鎖操作,沒有用互斥鎖解決帶來的加鎖解決性能消耗,這種絕佳方案是怎么做到的呢,需要的朋友可以參考下

前言引入

當(dāng)存在如下場景,兩個(gè)線程同時(shí)去將count值累加一萬次,那么如下代碼是否存在線程安全問題呢?

public class Test2 {
    public static void main(String[] args) throws InterruptedException {
        TestCount testCount = new TestCount();
        Thread T1 = new Thread(()->{
            testCount.add10k();
        });
        Thread T2 = new Thread(()->{
            testCount.add10k();
        });
        T1.start();
        T2.start();
        T1.join();
        T2.join();
        System.out.println(testCount.count);
    }
}
class TestCount{
    long count = 0;
    void add10k(){
        int idx = 0;
        while (idx++ < 10000){
            count += 1;
        }
    }
}

顯然是存在的,最終的結(jié)果顯然是小于兩萬的,原因是count值存在可見性問題,count+=1也存在原子性的問題。

一般思路都是將count采用volatile修飾,count+=1原子性問題采用synchronized互斥鎖解決。

class TestCount{
    volatile long count = 0;
    synchronized void add10k(){
        int idx = 0;
        while (idx++ < 10000){
            count += 1;
        }
    }
}

但是對于這種簡單的互斥操作,需要采用synchronized這種比較重的互斥鎖嗎?有沒有更優(yōu)的解決辦法呢?當(dāng)然存在,采用原子類無鎖方案能夠極大的提升性能

class TestCount{
    AtomicLong count = new AtomicLong(0);
     void add10k(){
        int idx = 0;
        while (idx++ < 10000){
            count.getAndIncrement();
        }
    }
}

原子類同樣能夠解決互斥性問題、原子性問題除此之外,因?yàn)樵宇愂菬o鎖操作,沒有用互斥鎖解決帶來的加鎖解決性能消耗,這種絕佳方案是怎么做到的呢?

無鎖方案實(shí)現(xiàn)原理

無鎖方案之所以能夠保證原子性,主要還是硬件保證,CPU為了解決并發(fā)問題,提供了CAS(Compare And Swap)指令即比較并交換,CAS一般包含三個(gè)參數(shù),共享變量的內(nèi)存地址A,用于比較的期望值B,更新共享變量C,當(dāng)共享變量的內(nèi)存地址A的值和共享變量B的值相等時(shí),才將共享變量的內(nèi)存地址A處的值更新為共享變量C。

將場景語義化如下

class SimpleCAS{
    int count;
    public synchronized int cas(int expect,int newCount){
        // 讀取count值
       int oldcount = count;
       // 讀取的count值和期望值比較
       if (oldcount == expect){
           count = newCount;
       }
       // 返回老值
       return oldcount;
    }
}

CAS指令判斷并不是一次性的,如果比較失敗又會(huì)重新取最新的值和期望值判斷直到成功。

class SimpleCAS{
    volatile int count;
    public synchronized int cas(int expect,int newCount){
        // 讀取count值
       int oldcount = count;
       // 讀取的count值和期望值比較
       if (oldcount == expect){
            count = newCount;
        }
       // 返回老值
       return oldcount;
    }
    // 自旋操作,執(zhí)行cas方法
    public void addOne(){
        int newCount = 0;
        do {
            newCount = count + 1;
        }while (count != cas(count,newCount));
    }
}

ABA問題

原子類雖然好用,但是一定需要的坑就是ABA問題,假如存在共享變量A值為5,線程T1將共享變量A的值改為2,而線程T2將共享變量A改為3,線程T3又將共享變量A改為2,那么對于線程T1來講共享變量A是沒有變的嗎?顯然不是,可能大多數(shù)場景我們并不關(guān)心ABA問題,對于基礎(chǔ)數(shù)據(jù)遞增可能認(rèn)為值不變就夠了,并不關(guān)心值是否已經(jīng)修改,但是對于引用類型呢,這就一定要注意ABA問題,兩個(gè)A雖然相等,但是屬性可能已經(jīng)發(fā)生變化。

原子類提供工具類解決ABA問題AtomicStampedReference和AtomicMarkableReference

public static void main(String[] args) throws InterruptedException {
    // 初始化原子類 定義初始引用和標(biāo)識(shí)
    // AtomicMarkableReference同理可得,只是將版本戳換成了boolean類型
    AtomicStampedReference<String> reference = new AtomicStampedReference<>("zhangsan",1001);
    /**
      * expectedReference 期望的引用
      * newReference     新的引用
      * expectedStamp    期望的版本戳
      * newStamp         新的版本戳
      * 只有當(dāng)期望引用和期望版本戳都符合實(shí)際版本戳和引用才能替換成功
    */
    reference.compareAndSet("zhangsan","lisi",1002,1003);
    // zhangsan 替換失敗的原因是期望版本戳和實(shí)際版本戳不匹配
    System.out.println(reference.getReference());
    reference.compareAndSet("zhangsan","lisi",1001,1002);
    // lisi 替換成功
    System.out.println(reference.getReference());
    reference.compareAndSet("lisi1","wangwu",1002,1003);
    // lisi 替換失敗的原因是期望引用和實(shí)際引用不匹配
    System.out.println(reference.getReference());
}

getAndIncrement源碼分析

/**
  * this 指當(dāng)前對象
  * valueOffset 指內(nèi)存地址偏移量
*/
public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
}
/**
  * this 指當(dāng)前對象
  * valueOffset 指內(nèi)存地址偏移量
*/
public final int getAndAddInt(Object var1, long var2, int var4) {
    int var5;
    do {
        // 就是讀取主內(nèi)存的值
        var5 = this.getIntVolatile(var1, var2);
        // this.compareAndSwapInt方法就是對應(yīng)上訴的cas方法,不過返回值是boolean類型
        // var5讀取的主內(nèi)存值
        // 更新成功返回true,跳出循環(huán)
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
    return var5;
}

原子工具類總覽

原子工具類是一個(gè)大家族,根據(jù)使用可以分為原子化基本數(shù)據(jù)類型、原子化的對象引用類型、原子化數(shù)組、原子化對象屬性更新器和原子化的累加器,方法都基本類似不需要刻意去記,需要用到的時(shí)候再來查就可以,但是需要有個(gè)印象,如下圖所示。

image-20220302143728329

到此這篇關(guān)于Java的原子類無鎖并發(fā)利器詳解的文章就介紹到這了,更多相關(guān)原子類無鎖并發(fā)利器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Spring Cloud Stream異常處理過程解析

    Spring Cloud Stream異常處理過程解析

    這篇文章主要介紹了Spring Cloud Stream異常處理過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-08-08
  • springboot簡單實(shí)現(xiàn)單點(diǎn)登錄的示例代碼

    springboot簡單實(shí)現(xiàn)單點(diǎn)登錄的示例代碼

    本文主要介紹了springboot簡單實(shí)現(xiàn)單點(diǎn)登錄的示例代碼,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • Spring Boot中定時(shí)任務(wù)Cron表達(dá)式的終極指南最佳實(shí)踐記錄

    Spring Boot中定時(shí)任務(wù)Cron表達(dá)式的終極指南最佳實(shí)踐記錄

    本文詳細(xì)介紹了SpringBoot中定時(shí)任務(wù)的實(shí)現(xiàn)方法,特別是Cron表達(dá)式的使用技巧和高級用法,從基礎(chǔ)語法到復(fù)雜場景,從快速啟用到調(diào)試驗(yàn)證,再到常見問題的解決,涵蓋了定時(shí)任務(wù)開發(fā)的全過程,感興趣的朋友一起看看吧
    2025-03-03
  • Java 生成PDF文檔的示例代碼

    Java 生成PDF文檔的示例代碼

    這篇文章主要介紹了Java 生成PDF文檔的示例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-08-08
  • Java實(shí)現(xiàn)設(shè)計(jì)模式之責(zé)任鏈模式

    Java實(shí)現(xiàn)設(shè)計(jì)模式之責(zé)任鏈模式

    責(zé)任鏈模式是一種行為設(shè)計(jì)模式,允許你將請求沿著處理鏈發(fā)送,然后處理者都可對其進(jìn)行處理,完成后可以再將其傳遞給下一個(gè)處理者。下面將會(huì)舉例說明什么是責(zé)任鏈模式,責(zé)任鏈模式該如何使用
    2022-08-08
  • Java使用Spire.PDF for Java實(shí)現(xiàn)旋轉(zhuǎn)PDF頁面

    Java使用Spire.PDF for Java實(shí)現(xiàn)旋轉(zhuǎn)PDF頁面

    在日常的文檔處理工作中,我們經(jīng)常會(huì)遇到 PDF 頁面方向不正確的問題,本文將深入探討如何利用 Java 實(shí)現(xiàn) PDF 頁面的旋轉(zhuǎn)操作,感興趣的小伙伴可以了解下
    2025-09-09
  • Java-文件File簡單實(shí)用方法(分享)

    Java-文件File簡單實(shí)用方法(分享)

    下面小編就為大家?guī)硪黄狫ava-文件File簡單實(shí)用方法(分享)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-08-08
  • java中http請求之restTemplate配置超時(shí)時(shí)間問題解決

    java中http請求之restTemplate配置超時(shí)時(shí)間問題解決

    這篇文章主要介紹了java中http請求之restTemplate配置超時(shí)時(shí)間,本文給大家分享三種解決方法,結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-05-05
  • java8 Stream list to Map key 重復(fù) value合并到Collectio的操作

    java8 Stream list to Map key 重復(fù) value合并到Collectio的操作

    這篇文章主要介紹了java8 Stream list to Map key 重復(fù) value合并到Collectio的操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • SpringBoot讀取配置的6種方式

    SpringBoot讀取配置的6種方式

    本文主要介紹了SpringBoot讀取配置的6種方式,主要包括使用默認(rèn)配置、使用application.properties文件、使用application.yml文件、使用@Value注解、使用Environment對象和使用ConfigurableEnvironment對象,感興趣的可以了解一下
    2023-08-08

最新評論

江陵县| 芮城县| 乌兰县| 封开县| 乌恰县| 自贡市| 襄城县| 襄樊市| 浦东新区| 乌恰县| 万盛区| 江达县| 封开县| 诏安县| 东光县| 庆阳市| 四子王旗| 四子王旗| 伊宁县| 苏州市| 泽州县| 临沧市| 本溪| 贡觉县| 科技| 资溪县| 商都县| 蒙城县| 长垣县| 桐柏县| 平武县| 巴塘县| 隆林| 通州区| 固镇县| 阿拉善左旗| 绥阳县| 米脂县| 大厂| 密云县| 黄石市|