1. 程式人生 > >JUC之atomic包

JUC之atomic包

相關概念之原子性:         原子性是指一個操作或多個操作要麼全部執行,且執行的過程不會被任何因素打斷,要麼都不執行。

atomic包是java.util.concurrent下的一個專門為執行緒安全設計的java包,該包下包含多個原子操作類:

atomic包下相對常用的類有:         AtomicInteger、AtomicIntegerArray、AtomicBoolean、AtomicLong、AtomicLongArray

atomic包下的類使用場景:         合適一些粒度比較小,如計數器這樣的需求用起來較方便;如果是那些比較大比較複雜的場景,建議還是使用鎖的方式。

宣告:本人只挑了上述類中的三個類來測試說明

AtomicInteger

Integer與AtomicInteger安全性對比測試:

package com.aspire.demo;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Atomic包與CAS演算法測試
 *
 * @author JustryDeng
 * @date 2018/10/25 16:14
 */
public class AtomicAndCASDemo {

    /** 執行緒數 */
    private static final Integer threadNum = 10000;

    /** Integer與AtomicInteger */
    private static Integer count = threadNum;
    private static AtomicInteger atomicInteger = new AtomicInteger(threadNum);

    /** 為了避免干擾,每個方法都使用自己對應的 */
    private static CountDownLatch countDownLatch1 = new CountDownLatch(threadNum);
    private static CountDownLatch countDownLatch2 = new CountDownLatch(threadNum);

    /** 為了避免干擾,每個方法都使用自己對應的 */
    private static CyclicBarrier cyclicBarrier1 = new CyclicBarrier(threadNum);
    private static CyclicBarrier cyclicBarrier2 = new CyclicBarrier(threadNum);

    /**
     * 多執行緒使用共享的Integer測試
     */
    private static void fa1() throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < threadNum; i++) {
            executorService.execute(() -> {
                try {
                    cyclicBarrier1.await();
                    count--;
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                } finally {
                    countDownLatch1.countDown();
                }
            });
        }
        executorService.shutdown();
        countDownLatch1.await();
        System.out.println("結果應為0, 本次執行結果count = " + count);
    }

    /**
     * 多執行緒使用共享的AtomicInteger測試
     */
    private static void fa2() throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < threadNum; i++) {
            executorService.execute(() -> {
                try {
                    cyclicBarrier2.await();
                    atomicInteger.getAndDecrement();
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                } finally {
                    countDownLatch2.countDown();
                }
            });
        }
        executorService.shutdown();
        countDownLatch2.await();
        System.out.println("結果應為0, 本次執行結果atomicInteger = " + atomicInteger);
    }

    /**
     * 主函式
     */
    public static void main(String[] args) throws InterruptedException {
        fa1();
        System.out.println();
        fa2();
    }
}

執行主函式,(某次)控制檯輸出:

說明:多次執行主函式,會發現fa1()輸出的結果幾乎總是大於0的錯誤的結果,fa2()輸出的結果總是等於0的正確的結果,           由此可見:AtomicInteger是執行緒安全的,Integer是非執行緒安全的

AtomicBoolean

AtomicBoolean說明:

        AtomicBoolean是一個執行緒安全的與Boolean對應的類。比較值得一說的是其public final boolean compareAndSet(boolean expect, boolean update)方法,該方法的功能是比較當前值atomicBoolean是否為期望值expect,如果是那麼將更新值update賦給當前值atomicBoolean;如果當前值atomicBoolean不為期望值expect,那麼直接返回false

如:atomicBoolean.compareAndSet(true, false)中,如果atomicBoolean為true,那麼就把false值賦給atomicBoolean      並返回true;如果atomicBoolean為false,那麼就直接返回false;

注:AtomicBoolean是原子性的類,如:.compareAndSet(boolean expect, boolean update)方法的原子性就體現在:判斷和      賦值隸屬於同一個原子操作;即:在其判斷當前值atomicBoolean等於期望值expect後、賦值前,這中間是不會有其他      執行緒對該atomicBoolean進行操作的。

注:更多方法可詳見API文件。

Boolean與AtomicBoolean安全性測試:

package com.aspire.demo;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Atomic包與CAS演算法測試
 *
 * @author JustryDeng
 * @date 2018/10/25 16:14
 */
public class AtomicAndCASDemo {

    /** 執行緒數 */
    private static final Integer threadNum = 10000;

    /** AtomicBoolean為保證多個.compareAndSet()的原子性,會用到鎖 */
    private static final ReentrantLock lock = new ReentrantLock();

    /** Boolean與AtomicBoolean */
    private static Boolean aBoolean = true;
    private static AtomicBoolean atomicBoolean = new AtomicBoolean(true);

    /** 為了避免干擾,每個方法都使用自己對應的 */
    private static CountDownLatch countDownLatch3 = new CountDownLatch(threadNum);
    private static CountDownLatch countDownLatch4 = new CountDownLatch(threadNum);

    /** 為了避免干擾,每個方法都使用自己對應的 */
    private static CyclicBarrier cyclicBarrier3 = new CyclicBarrier(threadNum);
    private static CyclicBarrier cyclicBarrier4 = new CyclicBarrier(threadNum);


    /**
     * 多執行緒使用共享的Boolean測試
     */
    private static void fa3() throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < threadNum; i++) {
            executorService.execute(() -> {
                try {
                    cyclicBarrier3.await();
                    aBoolean = !aBoolean;
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                } finally {
                    countDownLatch3.countDown();
                }
            });
        }
        executorService.shutdown();
        countDownLatch3.await();
        System.out.println("結果應為true, 本次執行結果aBoolean = " + aBoolean);
    }

    /**
     * 多執行緒使用共享的AtomicBoolean測試
     */
    private static void fa4() throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < threadNum; i++) {

            executorService.execute(() -> {
                try {
                    cyclicBarrier4.await();
                    // 雖然.compareAndSet()本身是原子性的,但是多個.compareAndSet()就不是原子性的了
                    // 這裡使用可重入鎖,保證多個.compareAndSet()的原子性
                    lock.lock();
                    try {
                        boolean result = atomicBoolean.compareAndSet(true, false);
                        if(!result) {
                            atomicBoolean.compareAndSet(false, true);
                        }
                    } finally {
                        lock.unlock();
                    }

                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                } finally {
                    countDownLatch4.countDown();
                }
            });
        }
        executorService.shutdown();
        countDownLatch4.await();
        System.out.println("結果應為true, 本次執行結果atomicBoolean = " + atomicBoolean);
    }


    /**
     * 主函式
     */
    public static void main(String[] args) throws InterruptedException {
        fa3();
        System.out.println();
        fa4();
    }
}

執行主函式,(某次)控制檯輸出:

說明:多次執行主函式,會發現fa4()總能輸出true;而fa3()有時輸出true,有時輸出false;由此可見:AtomicBoolean是          執行緒安全的,Boolean是非執行緒安全的

AtomicLongArray

Long[]與AtomicLongArray安全性測試:

package com.aspire.demo;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicLongArray;

/**
 * Atomic包與CAS演算法測試
 *
 * @author JustryDeng
 * @date 2018/10/25 16:14
 */
public class AtomicAndCASDemo {

    /** 執行緒數 */
    private static final Integer threadNum = 10000;

    /** Long[]與AtomicLongArray */
    private static Long[] longArray = new Long[1];
    private static AtomicLongArray atomicLongArray = new AtomicLongArray(1);

    /** 為了避免干擾,每個方法都使用自己對應的 */
    private static CountDownLatch countDownLatch5 = new CountDownLatch(threadNum);
    private static CountDownLatch countDownLatch6 = new CountDownLatch(threadNum);

    /** 為了避免干擾,每個方法都使用自己對應的 */
    private static CyclicBarrier cyclicBarrier5 = new CyclicBarrier(threadNum);
    private static CyclicBarrier cyclicBarrier6 = new CyclicBarrier(threadNum);

    /**
     * 多執行緒使用共享的Long[]測試
     */
    private static void fa5() throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < threadNum; i++) {
            final int index = i;
            executorService.execute(() -> {
                try {
                    cyclicBarrier5.await();
                    longArray[0] = longArray[0] + (long)index;
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                } finally {
                    countDownLatch5.countDown();
                }
            });
        }
        executorService.shutdown();
        countDownLatch5.await();
        System.out.println("結果應為49995000, 本次執行Long[]測試方法結果為" + longArray[0]);
    }

    /**
     * 多執行緒使用共享的AtomicLongArray測試
     */
    private static void fa6() throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < threadNum; i++) {
            final int index = i;
            executorService.execute(() -> {
                try {
                    cyclicBarrier6.await();
                    atomicLongArray.getAndAdd(0, index);
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                } finally {
                    countDownLatch6.countDown();
                }
            });
        }
        executorService.shutdown();
        countDownLatch6.await();
        System.out.println("結果應為49995000, 本次執行AtomicLongArray測試方法結果為" + atomicLongArray.get(0));
    }

    /**
     * 主函式
     */
    public static void main(String[] args) throws InterruptedException {
        longArray[0] =(long)0;
        atomicLongArray.set(0, 0);
        fa5();
        System.out.println();
        fa6();
    }
}

執行主函式,(某次)的輸出結果為:

說明:多次執行主函式,會發現fa5()輸出的結果幾乎總是錯誤的,fa6()輸出的結果總是正確的,          由此可見:AtomicLongArray是執行緒安全的,Long[]是非執行緒安全的

宣告:本文是學習筆記,主要學習自以下視訊微笑學習視訊            《Java多執行緒與併發實戰視訊課程》,齊毅 微笑如有不當之處,歡迎指正微笑本文已經被收錄進《程式設計師成長筆記(三)》,筆者JustryDeng