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[]是非執行緒安全的。