溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

高級并發(fā)編程系列之什么是原子類

發(fā)布時間:2021-10-23 15:36:55 來源:億速云 閱讀:146 作者:iii 欄目:編程語言

本篇內(nèi)容介紹了“高級并發(fā)編程系列之什么是原子類”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細閱讀,能夠?qū)W有所成!

1.考考你

相信作為資深程序員的你,對于AtomicInteger這樣的類,即以Atomic開始的類一定不會感到陌生。我們在翻看很多框架源碼、或者第三方組件都會經(jīng)常看到它們,如影隨形。

那么問題來了,像Atomicxxx這樣的類,到底是什么意思呢?從字面意思比較好理解,Atomic即原子性,那么Atomicxxx即原子類。講到這里,你一定還記得我們說過線程安全的三個基本要素,我們一起來回顧一下:可見性、原子性、有序性。原子類的原子性,講的就是這個原子性,于是你可以先記住一個結(jié)論:原子類,它是線程安全的類。

到這里有朋友可能會提出質(zhì)疑:你說線程安全,就線程安全嗎?我不服,你沒有講清楚。我不聽,我不聽......好吧,看官們莫急,且聽我一步一步分析,娓娓道來,話說......

#考考你:
1.你真的理解原子類的核心思想嗎
2.你在你的項目中,有直接用到過原子類嗎

2.案例

2.1.自增操作案例

2.1.1.普通變量版本

案例描述:

  • 定義一個普通的int型變量value,初始值為:0

  • 開啟兩個線程thread_1,thread_2并行執(zhí)行value++操作

  • 每個線程執(zhí)行 5000次,預(yù)期執(zhí)行結(jié)果: 2 * 5000 = 10000次

  • 通過觀察最終執(zhí)行結(jié)果,是否等于預(yù)期10000次

  • 結(jié)果不相等,說明線程不安全,原因是:value++操作不是一個原子性操作

package com.anan.edu.common.newthread.atomic;

/**
 * 普通 int變量 ++ 操作,非原子性,線程不安全
 *
 * @author ThinkPad
 * @version 1.0
 * @date 2020/11/29 8:27
 */
public class CommonIntDemo {

    /**
     * 普通成員變量
     */
    private int value = 0;

    public  void addValue(){
        value++;
    }

    public static void main(String[] args) throws InterruptedException {
        // 1.創(chuàng)建CommonIntDemo對象
        CommonIntDemo demo = new CommonIntDemo();

        // 2.創(chuàng)建2兩個線程,每個線程調(diào)用方法addValue 5000次
        // 預(yù)期value值結(jié)果等于:2 * 5000 = 10000
        int loopEnd = 5000;
        Thread thread_1 = new Thread(() -> {
            for(int i = 0; i < loopEnd; i++){
                demo.addValue();
            }
        }, "thread_1");

        Thread thread_2 = new Thread(() -> {
            for(int i = 0; i < loopEnd; i++){
                demo.addValue();
            }
        }, "thread_2");

        // 3.啟動執(zhí)行線程
        thread_1.start();
        thread_2.start();

        // 4.主線程等待子線程執(zhí)行完成,打印value值
        thread_1.join();
        thread_2.join();

        System.out.println("int型成員變量value最終結(jié)果:" + demo.value);
    }
}

執(zhí)行結(jié)果分析:

高級并發(fā)編程系列之什么是原子類

2.1.2.AtomicInteger版本

案例描述:

  • 定義一個AtomicInteger變量value,初始值為:0

  • 開啟兩個線程thread_1,thread_2并行執(zhí)行value.incrementAndGet()操作

  • 每個線程執(zhí)行 5000次,預(yù)期執(zhí)行結(jié)果: 2 * 5000 = 10000次

  • 通過觀察最終執(zhí)行結(jié)果,是否等于預(yù)期10000次

  • 結(jié)果相等,說明線程安全,原因是:原子類同時滿足了可見性、與原子性

package com.anan.edu.common.newthread.atomic;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 原子類AtomicInteger,實現(xiàn)自增操作,線程安全
 *
 * @author ThinkPad
 * @version 1.0
 * @date 2020/11/29 8:27
 */
public class AtomicIntegerDemo {

    /**
     * AtomicInteger成員變量
     */
    private AtomicInteger value = new AtomicInteger(0);

    public  void addValue(){
        value.incrementAndGet();
    }

    public static void main(String[] args) throws InterruptedException {
        // 1.創(chuàng)建AtomicIntegerDemo對象
        AtomicIntegerDemo demo = new AtomicIntegerDemo();

        // 2.創(chuàng)建2兩個線程,每個線程調(diào)用方法addValue 5000次
        // 預(yù)期value值結(jié)果等于:2 * 5000 = 10000
        int loopEnd = 5000;
        Thread thread_1 = new Thread(() -> {
            for(int i = 0; i < loopEnd; i++){
                demo.addValue();
            }
        }, "thread_1");

        Thread thread_2 = new Thread(() -> {
            for(int i = 0; i < loopEnd; i++){
                demo.addValue();
            }
        }, "thread_2");

        // 3.啟動執(zhí)行線程
        thread_1.start();
        thread_2.start();

        // 4.主線程等待子線程執(zhí)行完成,打印value值
        thread_1.join();
        thread_2.join();

        System.out.println("AtomicInteger型成員變量value最終結(jié)果:" + demo.value);
    }
}

執(zhí)行結(jié)果分析:

高級并發(fā)編程系列之什么是原子類

2.2.原子類底層原理分析

2.2.1.再次分析線程安全核心思想

通過比較普通類型int型變量自增操作,與原子型AtomicInteger型變量自增操作。我們看到應(yīng)用層代碼幾乎沒有差異,僅僅是通過AtomicInteger替換int實現(xiàn)自增操作,即保證了線程安全。那么AtomicInteger它是如何做到的呢?

要分析清楚AtomicInteger底層原理,還需要回到我們說過的線程安全基本要素:可見性、原子性、有序性。就是說不管通過什么手段,要實現(xiàn)線程安全,一定要滿足這三個基本要素,換句話說,滿足了三個基本要素,也即實現(xiàn)了線程安全。

那么我們就從這三個要素開始分析。首先看最容易理解的有序性,你還記得什么是有序性嗎?它是說線程內(nèi)有序,線程之間無序。有序性比較好理解,我們就不過多解釋了。

再來看可見性,同樣你還記得什么是可見性嗎?我們知道jmm內(nèi)存模型,每個線程都有自己的私人空間(工作內(nèi)存),所有線程共享公共空間(主內(nèi)存)。那么如果要保證某個變量在線程間的可見性,即當線程A操作該變量后,需要同步將變量值從私人空間同步到公共空間:工作內(nèi)存--->主內(nèi)存;同理其它線程在操作變量前,需要從公共空間將變量值同步到私人空間:主內(nèi)存--->工作內(nèi)存。java編程語法上給我們提供了一個關(guān)鍵字:volatile。用于實現(xiàn)可見性。你可能還需要下面這個圖:

高級并發(fā)編程系列之什么是原子類

最后再來看原子性,原子性你應(yīng)該還記得,我們上一篇:高級并發(fā)編程系列十二(一文搞懂cas)剛剛分享過。cas本質(zhì)上是不到黃河心不死,什么意思呢?即是不釋放cpu,循環(huán)操作,直到操作成功為止。我們是這么解釋的,你也應(yīng)該還記得對吧。而且我們還說過對于cas,它的操作原理是三個值:內(nèi)存值A(chǔ)、期望值B、更新值C。每次操作都會比較內(nèi)存值A(chǔ),是否等于期望值B、如果等于則將內(nèi)存值更新成值C,操作成功;如果內(nèi)存值A(chǔ),不等于期望值B,則操作失敗,進行下一次循環(huán)操作。你可能還需要下面這個圖:

高級并發(fā)編程系列之什么是原子類

好了到這里,我們可以一起來看AtomicInteger的源碼了??纯词欠駶M足我們說的可見性、原子性。進一步分析清楚AtomicInteger類線程安全的實現(xiàn)原理。下面我們通過截圖+文字描述的方式,方便你理解。

2.2.2.AtomicInteger類聲明

先來看AtomicInteger類的聲明,這一塊對于不熟悉的朋友可能比較難看懂,我們先截圖看一下。

高級并發(fā)編程系列之什么是原子類

2.2.3.方法incrementAndGet分析

通過類聲明部分源碼,我們看到線程安全的可見性,通過volatile關(guān)鍵字修飾value成員變量,已經(jīng)有了保障。那么原子性,又是如何保障的呢?答案是通過Unsafe工具類,進行cas操作來保障的??磮D:

高級并發(fā)編程系列之什么是原子類

2.3.juc原子類分類

相信通過上面的分析,你已經(jīng)理解了原子類線程安全的底層實現(xiàn)原理,如果你理解起來稍微還有點難度,我建議你多看兩遍。對于一個程序員來說,我們不應(yīng)該只會用用框架,底層思想和原理才是內(nèi)功。

那么關(guān)于原子類的底層分析,我們暫時放一放,下面我們一起來看一下juc包中提供的常見原子能力工具類。它們每一個的底層原理,都在上面分析過了,我就不再逐一分析了,只是簡單的列舉出來,如果你感興趣的話,可以找一兩個按照我上面的分析思路,自己分析一下,應(yīng)該會有意想不到的驚喜!

  • 基本原子類,代表:AtomicInteger、AtomicLong

  • 數(shù)組原子類,代表:AtomicIntegerArray、AtomicLongArray

  • 引用原子類,代表:AtomicReference<V>。關(guān)于引用原子類,稍微加一句:它可以把一個普通對象,包裝成具有原子能力的對象

  • 提供升級能力原子類,代表:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater

  • 累加器原子類,代表:LongAdder。關(guān)于累加器,稍微多加一句:它是jdk1.8開始后新加入的小伙伴,性能比起AtomicLong來說杠杠的。

“高級并發(fā)編程系列之什么是原子類”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI