Java中的Atomic包


Atomic包的作用

方便程序員在多線程環境下,無鎖的進行原子操作

Atomic包核心

Atomic包里的類基本都是使用Unsafe實現的包裝類,核心操作是CAS原子操作;

關於CAS

compare and swap,比較和替換技術,將預期值與當前變量的值比較(compare),如果相等則使用新值替換(swap)當前變量,否則不作操作;

現代CPU已廣泛支持CAS指令,如果不支持,那么JVM將使用自旋鎖,與互斥鎖一樣,兩者都需先獲取鎖才能訪問共享資源,但互斥鎖會導致線程進入睡眠,而自旋鎖會一直循環等待直到獲取鎖;

另外,有一點需要注意的是CAS操作中的ABA問題,即將預期值與當前變量的值比較的時候,即使相等也不能保證變量沒有被修改過,因為變量可能由A變成B再變回A,解決該問題,可以給變量增加一個版本號,每次修改變量時版本號自增,比較的時候,同時比較變量的值和版本號即可;

Atomic包主要提供四種原子更新方式

  • 原子方式更新基本類型;
  • 原子方式更新數組;
  • 原子方式更新引用;
  • 原子方式更新字段;

原子方式更新基本類型

以下三個類是以原子方式更新基本類型

  • AtomicBoolean:原子更新布爾類型。
  • AtomicInteger:原子更新整型。
  • AtomicLong:原子更新長整型。

以AtomicInteger為例,

package concurrency;
import java.util.concurrent.atomic.AtomicInteger;

public class AtomicIntegerTest {

    static AtomicInteger ai = new AtomicInteger(1);

    public static void main(String[] args) {
        //相當於i++,返回的是舊值,看方法名就知道,先獲取再自增
        System.out.println(ai.getAndIncrement());
        System.out.println(ai.get());
        //先自增,再獲取
        System.out.println(ai.incrementAndGet());
        System.out.println(ai.get());
        //增加一個指定值,先add,再get
        System.out.println(ai.addAndGet(5));
        System.out.println(ai.get());
        //增加一個指定值,先get,再set
        System.out.println(ai.getAndSet(5));
        System.out.println(ai.get());
    }

}

注意:Atomic包提供了三種基本類型的原子更新,剩余的Java的基本類型還有char,float和double等,其更新方式可以參考AtomicBoolean的思路來現,AtomicBoolean是把boolean轉成整型再調用compareAndSwapInt進行CAS來實現的,類似的short和byte也可以轉成整形,float和double可以利用Float.floatToIntBits,Double.doubleToLongBits轉成整形和長整形進行相應處理;

原子方式更新數組

以下三個類是以原子方式更新數組,

  • AtomicIntegerArray:原子更新整型數組里的元素。
  • AtomicLongArray:原子更新長整型數組里的元素。
  • AtomicReferenceArray:原子更新引用類型數組里的元素。

以AtomicIntegerArray為例,其方法與AtomicInteger很像,多了個數組下標索引;

package concurrency;

import java.util.concurrent.atomic.AtomicIntegerArray;

public class AtomicIntegerArrayTest {

    static int[] valueArr = new int[] { 1, 2 };

    //AtomicIntegerArray內部會拷貝一份數組
    static AtomicIntegerArray ai = new AtomicIntegerArray(valueArr);

    public static void main(String[] args) {
        ai.getAndSet(0, 3);
        //不會修改原始數組value
        System.out.println(ai.get(0));
        System.out.println(valueArr[0]);
    }

}

原子方式更新引用

以下三個類是以原子方式更新引用,與其它不同的是,更新引用可以更新多個變量,而不是一個變量;

  • AtomicReference:原子更新引用類型。
  • AtomicReferenceFieldUpdater:原子更新引用類型里的字段。
  • AtomicMarkableReference:原子更新帶有標記位的引用類型。

以AtomicReference為例,

package concurrency;

import java.util.concurrent.atomic.AtomicReference;

public class AtomicReferenceTest {

    public static AtomicReference<User> atomicUserRef = new AtomicReference<User>();

    public static void main(String[] args) {
        User user = new User("conan", 15);
        atomicUserRef.set(user);
        User updateUser = new User("Shinichi", 17);
        atomicUserRef.compareAndSet(user, updateUser);
        System.out.println(atomicUserRef.get().getName());
        System.out.println(atomicUserRef.get().getOld());
    }

    static class User {
        private String name;
        private int old;

        public User(String name, int old) {
            this.name = name;
            this.old = old;
        }

        public String getName() {
            return name;
        }

        public int getOld() {
            return old;
        }
    }
}

原子方式更新字段

以下三個類是以原子方式更新字段,

  • AtomicIntegerFieldUpdater:原子更新整型字段的更新器。
  • AtomicLongFieldUpdater:原子更新長整型字段的更新器。
  • AtomicStampedReference:原子更新帶有版本號的引用類型,用於解決使用CAS進行原子更新時,可能出現的ABA問題。

以AtomicIntegerFieldUpdater為例,

package concurrency;

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

public class AtomicIntegerFieldUpdaterTest {

    private static AtomicIntegerFieldUpdater<User> a = AtomicIntegerFieldUpdater
            .newUpdater(User.class, "old");

    public static void main(String[] args) {
        User conan = new User("conan", 10);
        System.out.println(a.getAndIncrement(conan));
        System.out.println(a.get(conan));
    }

    public static class User {
        private String name;
        //注意需要用volatile修飾
        public volatile int old;

        public User(String name, int old) {
            this.name = name;
            this.old = old;
        }

        public String getName() {
            return name;
        }

        public int getOld() {
            return old;
        }
    }
}

參考資料

http://ifeve.com/java-atomic/

《JAVA並發編程實戰》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM