JUC之Atomic

這個是在學習工作中的一些總結,若有不對之處歡迎大家指出。侵刪!
需要源碼聯繫QQ:1352057131
得之在俄頃,積之在平日。

原子類的作用

原子類和鎖的作用類似,都是爲了保證併發情況下的線程安全,不過原子類相比於鎖有一定的優勢:

粒度更細:原子變量可以把競爭範圍縮小到變量級別。
效率更高:使用原子類的效率比使用鎖的效率更高,但是高競爭下的情況除外。

原子類概覽

在這裏插入圖片描述

Atomic*基本類型原子類

AtomicInteger常用方法

//獲取當前的值並加上預期的值
atomicInteger.getAndAdd(int value);
//獲取當前值
atomicInteger.get();
//獲取當前值並自減
atomicInteger.getAndDecrement();
//獲取當前值並自增
atomicInteger.getAndIncrement();
//獲取當前值並設置新值
atomicInteger.getAndSet(int value);
//如果輸入的值等於預期值,則以原子的方式將該值設置爲輸入值(update)
atomicInteger.compareAndSet(int value,int update);

AtomicInteger示例

public class AtomicTest implements Runnable{
    private static  AtomicInteger atomicInteger = new AtomicInteger(0);
    private static int a = 0;
    public static void Atomicadd(){
        atomicInteger.getAndIncrement();
    }
    public static void add(){
        a++;
    }
    public static void main(String[] args) throws InterruptedException {
        Thread thread0 = new Thread(new AtomicTest());
        Thread thread1 = new Thread(new AtomicTest());
        thread0.start();
        thread1.start();
        thread0.join();
        thread1.join();
        System.out.println("atomicInteger="+atomicInteger);
        System.out.println("a="+a);
    }
    @Override
    public void run() {
        for (int i = 0; i <100000 ; i++) {
            Atomicadd();
            add();
        }
    }
}

運行結果:
從圖中可以看出AtomicInteger是線程安全的
從圖中可以看出AtomicInteger是線程安全的

Atomic*Array數組類型原子類

AtomicIntegerArray示例

public class AtomicTest{
    //聲明長度爲100的AtomicIntegerArray
    private static AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(100);
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        executorService.submit(new Add(atomicIntegerArray));
        executorService.submit(new Dec(atomicIntegerArray));
        Thread.sleep(1000);
        executorService.shutdown();
        for (int i = 0; i <atomicIntegerArray.length() ; i++) {

            if (atomicIntegerArray.get(i)!=0){
                System.out.println("發現不等於0的數");
            }
        }
        System.out.println("執行完畢");
    }
}
class Add implements Runnable{
    private AtomicIntegerArray atomicIntegerArray;

    public Add(AtomicIntegerArray atomicIntegerArray) {
        this.atomicIntegerArray = atomicIntegerArray;
    }
    @Override
    public void run() {
        for (int i = 0; i < atomicIntegerArray.length(); i++) {
            //加1
            atomicIntegerArray.getAndIncrement(i);
        }
    }
}
class Dec implements Runnable{
    private AtomicIntegerArray atomicIntegerArray;

    public Dec(AtomicIntegerArray atomicIntegerArray) {
        this.atomicIntegerArray = atomicIntegerArray;
    }
    @Override
    public void run() {
        for (int i = 0; i < atomicIntegerArray.length(); i++) {
            //減1
            atomicIntegerArray.getAndDecrement(i);
        }
    }
}

Atomic*Reference引用類型原子類

AtomicReference示例
在這裏插入圖片描述

Atomic*FieldUpdater升級類型原子類

AtomicIntegerFieldUpdater示例

public class AtomicIntegerFieldUpdaterTest implements Runnable{
    //第一個參數爲類  第二個參數爲要升級的變量名
    AtomicIntegerFieldUpdater<User> aifu = AtomicIntegerFieldUpdater.newUpdater(User.class,"number");
    static User tom = new User();
    static User tony = new User();
    public static void main(String[] args) throws InterruptedException {
        AtomicIntegerFieldUpdaterTest aifut = new AtomicIntegerFieldUpdaterTest();
               for (int i = 0; i < 1000; i++) {
            Thread thread0 = new Thread(aifut);
            Thread thread1 = new Thread(aifut);
            thread0.start();
            thread1.start();
            thread0.join();
            thread1.join();
        }
        System.out.println("tom"+tom.number);
        System.out.println("tony"+tony.number);
    }
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            tom.number++;
            aifu.getAndDecrement(tony);
        }
    }
    static class User{
        volatile int number;//分數
    }
}

Adder累加器

爲什麼要使用Adder

高併發下LongAdder比AtomicLong效率高,不過本質是空間換時間。
競爭激烈的時候,LongAdder把不同的線程對應到不同的Cell上進行修改,降低了衝突的概率,事多段鎖的理念,提高了併發性。

LongAdder示例

public class AdderTest implements Runnable{
    private static LongAdder longAdder = new LongAdder();
    public void Adder_Add(){
        longAdder.increment();
    }
    @Override
    public void run() {
        Adder_Add();
    }
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i <10000 ; i++) {
            executorService.submit(new AdderTest());
        }
        executorService.shutdown();
        while (!executorService.isTerminated()){
        }
        System.out.println(longAdder);
    }

LongAdder與AtomicLong性能對比
LongAdder:

```java
public class AdderTest implements Runnable{
    private static LongAdder longAdder = new LongAdder();
    public void Adder_Add(){
        for (int i = 0; i < 1000; i++) {
            longAdder.increment();
        }
    }
    @Override
    public void run() {
        Adder_Add();
    }
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(20);
        long start = System.currentTimeMillis();
        for (int i = 0; i <100000 ; i++) {
            executorService.submit(new AdderTest());
        }
        executorService.shutdown();
        while (!executorService.isTerminated()){
        }
        long end = System.currentTimeMillis();
        System.out.println("執行時間:"+(end-start));
    }
}

在這裏插入圖片描述

AtomicLong:

public class AtomicLongTest implements Runnable{
    private static AtomicLong atomicLong = new AtomicLong(0);
    @Override
    public void run() {
        for (int i = 0; i <1000 ; i++) {
            atomicLong.getAndIncrement();
        }
    }
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(20);
        long start = System.currentTimeMillis();
        for (int i = 0; i <100000 ; i++) {
            executorService.submit(new AtomicLongTest());
        }
        executorService.shutdown();
        while (!executorService.isTerminated()){
        }
        long end = System.currentTimeMillis();
        System.out.println("執行時間:"+(end-start));
    }
}

在這裏插入圖片描述
從運行結果可以看出LongAdder的效率明顯要高很多,原因如下:
AtomicLong每次加法都要flush和refresh導致很耗費資源
在這裏插入圖片描述
從圖中可以看出AtomicLong每次加法都要做同步,在高併發的情況下會導致衝突比較多因此效率下降,而LongAdder每個線程都會有自己的計數器,僅用來在自己線程內計數,這樣就不會受到其他線程計數器的干擾。如下圖
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
從圖中可以看出,第一個線程的計數器數值,也就是str’,爲1的時候,可能線程2的計數器ctr”的數值已經是3了,他們之間並不存在競爭關係,所以在加和的過程中根本不需要同步機制,也不需要flush和refresh。也沒有公共的counter來計數。只是到最後sum的時候纔會同步將所有的數值累加起來 。
適用場景

適用於統計求和計數的場景,而且LongAdder基本只提供了add方法,AtomicLong還具有cas操作。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章