您的位置:首頁(yè) > 軟件教程 > 教程 > 學(xué)習(xí)Java并發(fā)編程中的Atomic原子庫(kù)

學(xué)習(xí)Java并發(fā)編程中的Atomic原子庫(kù)

來(lái)源:好特整理 | 時(shí)間:2024-05-21 09:46:03 | 閱讀:79 |  標(biāo)簽: T MIC VA Ato AVA v S C 算法 編程 AV java   | 分享到:

一、寫(xiě)在開(kāi)頭 在前面的博文中我們學(xué)習(xí)了volatile關(guān)鍵字,知道了它可以保證有序性和可見(jiàn)性,但無(wú)法保障原子性,結(jié)局原子性問(wèn)題推薦使用synchronized、Lock或者AtomicInteger;我們還學(xué)習(xí)過(guò)CAS算法,在那篇博文中我們同樣也提及atomic。那么今天,我們就來(lái)好好學(xué)一學(xué)Atom

在之前的博文中,我們已經(jīng)學(xué)習(xí)了volatile關(guān)鍵字,了解到它可以保證有序性和可見(jiàn)性,但無(wú)法保障原子性。解決原子性問(wèn)題時(shí),推薦使用synchronized、Lock或者AtomicInteger。我們還學(xué)習(xí)過(guò)CAS算法,提及了atomic。今天,我們將深入學(xué)習(xí)Atomic原子庫(kù),這是一個(gè)基于CAS算法實(shí)現(xiàn)的高效并發(fā)工具庫(kù)。

并發(fā)包java.util.concurrent的原子類都存放在java.util.concurrent.atomic中。如下圖所示:

學(xué)習(xí)Java并發(fā)編程中的Atomic原子庫(kù)

一、Atomic釋義

Atomic翻譯為“原子”,何為原子?在化學(xué)領(lǐng)域的原子被認(rèn)為是構(gòu)成化學(xué)反應(yīng)的最小微觀粒子,是不可分割的最小單位。偉大的Doug Lea大師,將并發(fā)的一些類以此單詞開(kāi)頭命名,一語(yǔ)中的!

  • 原子性在程序中所表達(dá)的意思是:一個(gè)或者多個(gè)操作在CPU執(zhí)行的過(guò)程中不被中斷的特性。
  • 原子操作在程序中表達(dá)的意思是:即最小不可拆分的操作,也就是說(shuō)操作一旦開(kāi)始,就不能被打斷,直到操作完成。

二、四大原子分類

我們根據(jù)操作的數(shù)據(jù)類型可以將JUC包中的原子類做如下的4種劃分:

2.1 基本類型

原子操作的基本類型主要可分為:

  1. AtomicBoolean:布爾型原子類;
  2. AtomicInteger:整型原子類;
  3. AtomicLong:長(zhǎng)整型原子類;

這三種方式用法幾乎相同,都是以原子更新的方式操作基本類型,我們?cè)谶@里以AtomicInteger為例看一下它的使用與原理。

1)AtomicInteger的常用方法

public final int get() //獲取當(dāng)前的值
public final int getAndSet(int newValue)//獲取當(dāng)前的值,并設(shè)置為newValue
public final int getAndIncrement()//獲取當(dāng)前的值,并自增
public final int incrementAndGet()//增加 1,并獲取新值,注意與上面方法區(qū)分
public final int getAndDecrement() //獲取當(dāng)前的值,并自減
public final int getAndAdd(int delta) //獲取當(dāng)前的值,并加上預(yù)期的值delta
boolean compareAndSet(int expect, int update) //如果輸入的數(shù)值等于預(yù)期值,則以原子方式將該值設(shè)置為輸入值(update)
public final void lazySet(int newValue)//最終設(shè)置為newValue,使用 lazySet 設(shè)置之后可能導(dǎo)致其他線程在之后的一小段時(shí)間內(nèi)還是可以讀到舊的值.

2)AtomicInteger的使用案例

public class Test {
    public static void main(String[] args) {
        //臨時(shí)值
        int temvalue = 0;
        AtomicInteger i = new AtomicInteger(0);
        temvalue = i.getAndSet(3);
        System.out.println("temvalue:" + temvalue + ";  i:" + i); //temvalue:0;  i:3
        temvalue = i.getAndIncrement();
        System.out.println("temvalue:" + temvalue + ";  i:" + i); //temvalue:3;  i:4
        temvalue = i.getAndAdd(5);
        System.out.println("temvalue:" + temvalue + ";  i:" + i); //temvalue:4;  i:9
        temvalue = i.incrementAndGet();
        System.out.println("temvalue:" + temvalue + ";  i:" + i); //temvalue:10;  i:10
    }
}

這里需要注意的一點(diǎn)是getAndIncrement()方法與incrementAndGet(),一個(gè)先獲值后自增,一個(gè)先自增后獲值。

3)AtomicInteger的底層原理

我們以getAndIncrement()為例,去跟入它的底層代碼會(huì)發(fā)現(xiàn),其內(nèi)部是通過(guò)調(diào)用UnSafe類的靜態(tài)方法getUnsafe實(shí)現(xiàn)的。UnSafe類在CAS算法的時(shí)候有提及,后面我們?cè)賳为?dú)學(xué)習(xí)它,其底層是通過(guò)CAS,原子性的進(jìn)行增加值。

public final int getAndIncrement() {
    // 使用Unsafe類中的getAndAddInt方法原子地增加AtomicInteger的當(dāng)前值
    // 第一個(gè)參數(shù)this是AtomicInteger的當(dāng)前實(shí)例
    // 第二個(gè)參數(shù)valueOffset是一個(gè)偏移量,它指示在AtomicInteger對(duì)象中的哪個(gè)位置可以找到實(shí)際的int值
    // 第三個(gè)參數(shù)1表示要加到當(dāng)前值上的值(即增加的值)
    // 此方法返回的是增加前的原始值
    return unsafe.getAndAddInt(this, valueOffset, 1);
}

Unsafe 類是Java中的一個(gè)特殊類,用于執(zhí)行低級(jí)、不安全的操作。getAndIncrement方法就是利用了Unsafe類提供的CAS(Compare-And-Swap)操作來(lái)實(shí)現(xiàn)原子的increment操作。CAS是一種常用的無(wú)鎖技術(shù),允許在多線程環(huán)境中原子地更新值。

2.2 數(shù)組類型

原子操作根據(jù)數(shù)組類型,可以分為如下幾種:

  1. AtomicIntegerArray:整形數(shù)組原子類
  2. AtomicLongArray:長(zhǎng)整形數(shù)組原子類
  3. AtomicReferenceArray:引用類型數(shù)組原子類

這三種同樣很類似,我們以AtomicIntegerArray為例來(lái)介紹一下。

1)AtomicIntegerArray的常用方法

public final int get(int i) //獲取 index=i 位置元素的值
public final int getAndSet(int i, int newValue)//返回 index=i 位置的當(dāng)前的值,并將其設(shè)置為新值:newValue
public final int getAndIncrement(int i)//獲取 index=i 位置元素的值,并讓該位置的元素自增
public final int getAndDecrement(int i) //獲取 index=i 位置元素的值,并讓該位置的元素自減
public final int getAndAdd(int i, int delta) //獲取 index=i 位置元素的值,并加上預(yù)期的值
boolean compareAndSet(int i, int expect, int update) //如果輸入的數(shù)值等于預(yù)期值,則以原子方式將 index=i 位置的元素值設(shè)置為輸入值(update)
public final void lazySet(int i, int newValue)//最終 將index=i 位置的元素設(shè)置為newValue,使用 lazySet 設(shè)置之后可能導(dǎo)致其他線程在之后的一小段時(shí)間內(nèi)還是可以讀到舊的值.

2)AtomicIntegerArray的使用案例

public class Test {
    public static void main(String[] args) {
        int temvalue = 0;
        int[] nums = { 1, 2, 3, 4, 5, 6 };
        AtomicIntegerArray i = new AtomicIntegerArray(nums);
        for (int j = 0; j < nums.length; j++) {
            System.out.print(i.get(j));
        }
        System.out.println();
        temvalue = i.getAndSet(0, 2);
        System.out.println("temvalue:" + temvalue + ";  i:" + i);
        temvalue = i.getAndIncrement(0);
        System.out.println("temvalue:" + temvalue + ";  i:" + i);
        temvalue = i.getAndAdd(0, 5);
        System.out.println("temvalue:" + temvalue + ";  i:" + i);
    }
}

輸出:

123456
temvalue:1;  i:[2, 2, 3, 4, 5, 6]
temvalue:2;  i:[3, 2, 3, 4, 5, 6]
temvalue:3;  i:[8, 2, 3, 4, 5, 6]

2.3 引用類型

除了如上的2種原子類外,atomic包中還提供了引用類型原子類。大概為如下幾種:

  1. AtomicReference:原子更新引用類型,使用AtomicReference類保證對(duì)象之間的原子性,把多個(gè)變量放到一個(gè)對(duì)象里面進(jìn)行CAS操作;
  2. AtomicStampedReference:原子更新帶有版本號(hào)的引用類型。該類將整數(shù)值與引用關(guān)聯(lián)起來(lái),可用于解決原子的更新數(shù)據(jù)和數(shù)據(jù)的版本號(hào),可以解決使用CAS進(jìn)行原子更新時(shí)可能出現(xiàn)的ABA問(wèn)題;
  3. AtomicMarkableReference:原子更新帶有標(biāo)記的引用類型,該類將boolean標(biāo)記與引用關(guān)聯(lián)起來(lái)。

常用方法又上述兩種類型一致,這里不再贅述,我們直接寫(xiě)一個(gè)demo感受一下它的使用吧。

public class TestAtomicReference {

    private static AtomicReference reference = new AtomicReference<>();

    public static void main(String[] args) {
        User user1 = new User("小明", 18);
        reference.set(user1);
        User user2 = new User("小華",20);
        User user = reference.getAndSet(user2);
        System.out.println(user);
        System.out.println(reference.get());
    }

    static class User {
        private String userName;
        private int age;

        public User(String userName, int age) {
            this.userName = userName;
            this.age = age;
        }

        @Override
        public String toString() {
            return "User{" +
                    "userName='" + userName + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
}

輸出:

User{userName='小明', age=18}
User{userName='小華', age=20}

2.4 對(duì)象的屬性修改類型

除了原子更新對(duì)象(引用類型)外,atomic中還提供了更新對(duì)象的屬性字段的原子類:

  1. AtomicIntegerFieldUpdater:原子更新整形字段的更新器;
  2. AtomicLongFieldUpdater:原子更新長(zhǎng)整形字段的更新器;
  3. AtomicReferenceFieldUpdater:原子更新引用類型里的字段的更新器。

如果想要原子的更新對(duì)象的屬性,實(shí)現(xiàn)起來(lái)較上面幾種類型略微復(fù)雜一下,大概分為兩步:

步驟1??
通過(guò)靜態(tài)方法newUpdater創(chuàng)建一個(gè)更新器,并且設(shè)置想要更新的類和字段;

步驟2??
字段必須使用public volatile進(jìn)行修飾;

以AtomicIntegerFieldUpdater為例,我們寫(xiě)一個(gè)測(cè)試類感受一下。

public class TestAtomicIntegerFieldUpdater {
    //創(chuàng)建一個(gè)age的更新器
    private static AtomicIntegerFieldUpdater updater = AtomicIntegerFieldUpdater.newUpdater(User.class,"age");

    public static void main(String[] args) {
        User user = new User("小明", 17);
        int oldValue = updater.getAndAdd(user, 1);
        System.out.println(oldValue);//17
        System.out.println(updater.get(user));//18
    }

    static class User {
        private String userName;
        public volatile int age;

        public User(String userName, int age) {
            this.userName = userName;
            this.age = age;
        }

        @Override
        public String toString() {
            return "User{" +
                    "userName='" + userName + '\'' +
                    ", age=" + age +
                    '}';
        }
    }

通過(guò)AtomicIntegerFieldUpdater.newUpdater(User.class,"age")創(chuàng)建一個(gè)age的更新器,然后調(diào)用getAndAdd(user, 1)進(jìn)行年齡加1操作,從17歲變?yōu)?8歲。

小編推薦閱讀

好特網(wǎng)發(fā)布此文僅為傳遞信息,不代表好特網(wǎng)認(rèn)同期限觀點(diǎn)或證實(shí)其描述。

相關(guān)視頻攻略

更多

掃二維碼進(jìn)入好特網(wǎng)手機(jī)版本!

掃二維碼進(jìn)入好特網(wǎng)微信公眾號(hào)!

本站所有軟件,都由網(wǎng)友上傳,如有侵犯你的版權(quán),請(qǐng)發(fā)郵件[email protected]

湘ICP備2022002427號(hào)-10 湘公網(wǎng)安備:43070202000427號(hào)© 2013~2025 haote.com 好特網(wǎng)