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();
}
//等待上面50个线程全部计算完成后,再去获得最终值

//暂停几秒钟线程
//try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }

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 修饰符。
public volatile int money = 0;//钱数

public void add()
{
money++;
}

//因为对象的属性修改类型原子类都是抽象类,所以每次使用都必须
// 使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性。

AtomicIntegerFieldUpdater<BankAccount> fieldUpdater =
AtomicIntegerFieldUpdater.newUpdater(BankAccount.class,"money");

//不加synchronized,保证高性能原子性,局部微创小手术
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);//1
longAccumulator.accumulate(3);//4
System.out.println(longAccumulator.get());
}
}

4. 原子操作总结

AtomicLong: 原理CAS+自旋+volatile。进行计数,能够保证并发安全。在高并发下,性能急剧下降,自旋操作影响性能。
LongAdder: 原理CAS+Base+Cell+volatile。进行计数,能够保证并发安全。在高并发下,性能也很好。但是空间消耗略大。