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

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

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

前言引入

當存在如下場景,兩個線程同時去將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)的解決辦法呢?當然存在,采用原子類無鎖方案能夠極大的提升性能

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

原子類同樣能夠解決互斥性問題、原子性問題除此之外,因為原子類是無鎖操作,沒有用互斥鎖解決帶來的加鎖解決性能消耗,這種絕佳方案是怎么做到的呢?

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

無鎖方案之所以能夠保證原子性,主要還是硬件保證,CPU為了解決并發(fā)問題,提供了CAS(Compare And Swap)指令即比較并交換,CAS一般包含三個參數(shù),共享變量的內(nèi)存地址A,用于比較的期望值B,更新共享變量C,當共享變量的內(nèi)存地址A的值和共享變量B的值相等時,才將共享變量的內(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指令判斷并不是一次性的,如果比較失敗又會重新取最新的值和期望值判斷直到成功。

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ù)遞增可能認為值不變就夠了,并不關(guān)心值是否已經(jīng)修改,但是對于引用類型呢,這就一定要注意ABA問題,兩個A雖然相等,但是屬性可能已經(jīng)發(fā)生變化。

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

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

getAndIncrement源碼分析

/**
  * this 指當前對象
  * valueOffset 指內(nèi)存地址偏移量
*/
public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
}
/**
  * this 指當前對象
  * 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;
}

原子工具類總覽

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

image-20220302143728329

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

相關(guān)文章

  • Java中的Semaphore源碼分析

    Java中的Semaphore源碼分析

    這篇文章主要介紹了Java中的Semaphore源碼分析,Semaphore是一個訪問公共資源的線程數(shù)量如限流、停車等,它是一個基于AQS實現(xiàn)的共享鎖,主要是通過控制state變量來實現(xiàn),需要的朋友可以參考下
    2023-11-11
  • SpringCloud配置客戶端ConfigClient接入服務(wù)端

    SpringCloud配置客戶端ConfigClient接入服務(wù)端

    這篇文章主要為大家介紹了SpringCloud配置客戶端ConfigClient接入服務(wù)端,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-08-08
  • Spring?Security中使用authorizeRequests遇到的問題小結(jié)

    Spring?Security中使用authorizeRequests遇到的問題小結(jié)

    Spring?是非常流行和成功的?Java?應(yīng)用開發(fā)框架,Spring?Security?正是?Spring?家族中的成員,這篇文章主要介紹了Spring?Security中使用authorizeRequests遇到的問題,需要的朋友可以參考下
    2023-02-02
  • java利用oss實現(xiàn)下載功能

    java利用oss實現(xiàn)下載功能

    這篇文章主要為大家詳細介紹了java利用oss實現(xiàn)下載功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-10-10
  • idea前后跳轉(zhuǎn)箭頭的快捷鍵

    idea前后跳轉(zhuǎn)箭頭的快捷鍵

    這篇文章主要介紹了idea前后跳轉(zhuǎn)箭頭的快捷鍵,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • Java之Error與Exception的區(qū)別案例詳解

    Java之Error與Exception的區(qū)別案例詳解

    這篇文章主要介紹了Java之Error與Exception的區(qū)別案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-09-09
  • SpringBoot?AOP?Redis實現(xiàn)延時雙刪功能實戰(zhàn)

    SpringBoot?AOP?Redis實現(xiàn)延時雙刪功能實戰(zhàn)

    本文主要介紹了SpringBoot?AOP?Redis實現(xiàn)延時雙刪功能實戰(zhàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-08-08
  • Java基于對象流實現(xiàn)銀行系統(tǒng)

    Java基于對象流實現(xiàn)銀行系統(tǒng)

    這篇文章主要為大家詳細介紹了Java基于對象流實現(xiàn)銀行系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-09-09
  • Java編程簡單應(yīng)用

    Java編程簡單應(yīng)用

    本文主要介紹了三個簡單Java小程序———1、HelloWorld(HelloWorld的來源);2、輸出個人信息3、輸出特殊圖案。具有很好的參考價值,下面跟著小編一起來看下吧
    2017-02-02
  • 學習Java之File文件操作方法

    學習Java之File文件操作方法

    這篇文章我們主要學習如何實現(xiàn)IO流的具體操作,但很多時候,IO流都會操作一個文件,所以我們需要先學習在Java中如何操作文件,包括文件及文件夾的創(chuàng)建、遍歷、刪除等,有了文件操作的基礎(chǔ),我們才能更好地操作IO流,文中有詳細的代碼示例,需要的朋友可以參考下
    2023-09-09

最新評論