相关概念之原子性:
原子性是指一个操作或多个操作要么全部执行,且执行的过程不会被任何因素打断,要么都不执行。
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[]是非线程安全的。