1. 概述
多线程下使用原子类来保证线程安全
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class AtomicIntegerTest {
AtomicInteger atomicInteger = new AtomicInteger(0);
public int getNum(){ return atomicInteger.get(); }
public void addNum(){ atomicInteger.getAndIncrement(); } }
|
volatile能解决多线程并发安全中的可见性问题,但是不能解决原子性问题。
atomic原子类通过CAS+volatile来保证并发安全。CAS保证原子性,volatile保证原子性。
2. 分类
原子类位于java.util.concurrent.atomic包下面。可以分为以下几类,基本类型的原子类,数组类型的原子类,引用类型的原子类和对象的属性修改原子类四类。
2.1 基本类型原子类
AtomicBoolean, AtomicInteger, AtomicLong
常用的API:
获取值,自增,自减,CAS
使用举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public class AtomicIntegerDemo { public static final int SIZE = 50;
public static void main(String[] args) throws InterruptedException { MyNumber myNumber = new MyNumber(); CountDownLatch countDownLatch = new CountDownLatch(SIZE);
for (int i = 1; i <=SIZE; i++) { new Thread(() -> { try { for (int j = 1; j <=1000; j++) { myNumber.addPlusPlus(); } } finally { countDownLatch.countDown(); } },String.valueOf(i)).start(); }
countDownLatch.await();
System.out.println(Thread.currentThread().getName()+"\t"+"result: "+myNumber.atomicInteger.get()); } }
|
补充:CountDownLatch计数器,可以使一个或多个线程等待其他线程执行完毕后再执行。
CountDownLatch定义了一个计数器和阻塞队列,当计数器减为0就唤醒阻塞队列中的线程。使用await()方法主动将当前线程阻塞(放入阻塞队列中)
2.2 数组类型原子类
AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray
常用的API
1 2 3
| getAndSet(int i, int newValue) getAndIncrement(int i) getAndAdd(int i, int delta)
|
2.3 引用类型原子类
AtomicReference
原子引用类
AtomicStampedReference
原子邮戳(版本号)引用类,携带版本号的的原子引用。
AtomicMarkableReference
原子标记引用类,带标记(将版本号简化为true,false的标记)的原子引用。
常用API
1 2 3 4 5
| public boolean isMarked(); public boolean compareAndSet(V expectedReference, V newReference, boolean expectedMark, boolean newMark)
|
2.4 对象的属性修改原子类
AtomicIntegerFiledUpdater, AtomicLongFiledUpdater, AtomicReferenceFiledUpdater
基于反射实现的,可以对volatile属性进行更新。因此需要更新的属性必须用volatile修饰。(volatile重要用法)
常用的API
1 2 3
| static AtomicIntegerFieldUpdater<U> newUpdater(Class<U> tclass,String fieldName) public int getAndIncrement(T obj) static AtomicReferenceFieldUpdater<U,W> newUpdater(Class<U> tclass,Class<W> vclass,String fieldName)
|
举例: AtomicIntegerFiledUpdater
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| class BankAccount { String bankName = "CCB";
public volatile int money = 0;
public void add() { money++; }
AtomicIntegerFieldUpdater<BankAccount> fieldUpdater = AtomicIntegerFieldUpdater.newUpdater(BankAccount.class,"money");
public void transMoney(BankAccount bankAccount) { fieldUpdater.getAndIncrement(bankAccount); }
}
|
举例: AtomicReferenceFiledUpdater
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| class MyVar { public volatile Boolean isInit = Boolean.FALSE;
AtomicReferenceFieldUpdater<MyVar,Boolean> referenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(MyVar.class,Boolean.class,"isInit");
public void init(MyVar myVar) { if (referenceFieldUpdater.compareAndSet(myVar,Boolean.FALSE,Boolean.TRUE)) { System.out.println(Thread.currentThread().getName()+"\t"+"----- start init,need 2 seconds"); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"\t"+"----- over init"); }else{ System.out.println(Thread.currentThread().getName()+"\t"+"----- 已经有线程在进行初始化工作。。。。。"); } } }
|
3. 原子操作增强类LongAdder, LongAccumulator
在高并发的情况下,使用Atomic原子类会导致线程竞争的时候CAS操作自旋消耗CPU的性能,这个时候用LongAdder性能更好,代价是空间消耗更多,以空间换时间。
LongAdder只能加减法,且从0开始。
LongAccumulator更强大,可以自定义运算。
3.1 LongAdder为什么这么快?
在低并发无竞争的情况下跟AtomicLong一样,对同一个base操作。
当高并发的情况下:
LongAdder的核心思想就是以空间换时间,分散热点。在Atomic原子类的基础上将value值分散到多个cell中去(cell数组),不同的线程会命中不同的cell,每个线程对自己对应的cell进行操作,减少冲突从而减少CAS自旋。最后将各个celll中的变量累加返回即可。
3.2 LongAdder使用
常用的API
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| public class LongAdderAPIDemo { public static void main(String[] args) { LongAdder longAdder = new LongAdder(); longAdder.increment(); longAdder.increment(); longAdder.increment(); System.out.println(longAdder.sum()); LongAccumulator longAccumulator = new LongAccumulator(new LongBinaryOperator() { @Override public long applyAsLong(long left, long right) { return left + right; } },0); longAccumulator.accumulate(1); longAccumulator.accumulate(3); System.out.println(longAccumulator.get()); } }
|
4. 原子操作总结
AtomicLong: 原理CAS+自旋+volatile。进行计数,能够保证并发安全。在高并发下,性能急剧下降,自旋操作影响性能。
LongAdder: 原理CAS+Base+Cell+volatile。进行计数,能够保证并发安全。在高并发下,性能也很好。但是空间消耗略大。