1. 什么是原子类
原子类是指java.util.concurrent.atomic下的类
其中这十六个类可以分为以下
- 基本类型原子类
- 数据类型原子类
- 引用类型原子类
- 对象的属性修改原子类
- 原子操作增强类
1.1 基本类型原子类
基本类型原子类包括
- AtomicInteger
- AtomicBoolean
- AtomicLong
相关API
- public final int get()//获取当前的值
- public final int getAndSet(int newValue)//获取当前的值,并设置新的值
- public final int getAndIncrement()//获取当前的值,并自增
- public final int getAndDecrement)//获取当前的值,并自减
- public final int getAndAdd(int delta)//获取当前的值,并加上预期的值
- boolean compareAndSet(int expect, int update)//如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update)
class MyNumber{
AtomicInteger atomicInteger = new AtomicInteger();
public void addPlusPlus(){
atomicInteger.getAndIncrement();
}
}
public class AtomicIntegerDemo {
public static final int SIZE = 50;
@SneakyThrows
public static void main(String[] args) {
MyNumber myNumber = new MyNumber();
CountDownLatch countDownLatch = new CountDownLatch(SIZE);
for (int i = 0; i < SIZE; i++) {
new Thread(() -> {
try {
for (int j = 0; 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是用来计算线程是否完成的
1.2 数组类型原子类
数组类型原子类包括以下
- AtomicIntegerArray
- AtomicLongArray
- AtomicReferenceArray
这种类型比较简单,上当与将基本类型原子类进一步变成数组
public class AtomicIntegerArrayDemo {
public static void main(String[] args) {
AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(new int[]{1, 2, 3, 4, 5});
for (int i = 0; i < atomicIntegerArray.length(); i++) {
System.out.println(atomicIntegerArray.get(i));
}
}
}
1.3 引用类型原子类
引用类型原子类包括以下
- AtomicReference
- AtomicStampedReference
- 携带版本号的引用类型原子类,可以解决ABA问题
- 解决修改过几次
- 状态戳原子引用
- AtomicMarkableReference
- 原子更新带有标记位的引用类型对象
- 解决是否修改过
- 它的定义是将状态戳简化为true|false
- 状态戳原子引用
1.4 对象的属性修改原子类
- AtomicIntegerFieldUpdater:原子更新对象中的int类型字段的值
- AtomicLongFieldUpdater:原子更新对象中Long类型字段的值
- AtomicReferenceFieldUpdater:原子更新引用类型字段的值
class BankAccount {
String bandName = "CCB";
public volatile int money = 0;
AtomicIntegerFieldUpdater<BankAccount> fieldUpdater =
AtomicIntegerFieldUpdater.newUpdater(BankAccount.class, "money");
public void transMoney(BankAccount bankAccount) {
fieldUpdater.getAndIncrement(bankAccount);
}
}
public class AtomicReferenceFieldUpdaterDemo {
public static void main(String[] args) throws InterruptedException {
BankAccount bankAccount = new BankAccount();
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 1; i <= 10; i++) {
new Thread(() -> {
try {
for (int j = 0; j < 1000; j++) {
bankAccount.transMoney(bankAccount);
}
}finally {
countDownLatch.countDown();
}
}).start();
}
countDownLatch.await();
System.out.println(Thread.currentThread().getName() + "\t" + "result:" + bankAccount.money);
}
}
class MyVar{
public volatile Boolean isInit = Boolean.FALSE;
AtomicReferenceFieldUpdater<MyVar, Boolean> referenceFieldUpdater =
AtomicReferenceFieldUpdater.newUpdater(MyVar.class, Boolean.class, "isInit");
@SneakyThrows
public void init(MyVar myVar){
if (referenceFieldUpdater.compareAndSet(myVar, Boolean.FALSE, Boolean.TRUE)){
System.out.println(Thread.currentThread().getName() + "\t" + "------ start init, need 2 s");
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + "\t" + "====== over init");
}else {
System.out.println(Thread.currentThread().getName() + "\t" + "====== 已有线程初始化");
}
}
}
public class AtomicReferenceFieldUpdaterDemo {
public static void main(String[] args) {
MyVar myVar = new MyVar();
for (int i = 0; i < 5; i++) {
new Thread(() -> {
myVar.init(myVar);
}, String.valueOf(i)).start();
}
}
}
1.5 原子操作增强类
原子操作增强类是指:
- DoubleAccumulator
- DoubleAdder
- LongAccumulator
- LongAdder
原子操作类是JDK1.8出现的
在阿里巴巴Java开发手册中提到:如果是count++操作,使用如下类实现AtomicInteger count = new AtomicInteger();
count.addAndGet(1);如果是JDK8,推荐使用LongAdder对象,这比AtomicLong性能更好(减少乐观锁)
这类通常是最好的AtomicLong
当多个线程共同更新和用于用途,如收集统计,不为细粒度的同步控制。在低更新争用下,两个类具有相似的特征。但在高争用,预计这一类的吞吐量显着更高,在更高的空间消耗的费用。
就以LongAdder而言,常用API如下
方法名 | 说明 |
---|---|
void add(long x) | 将当前的value加x |
void increment() | 将当前的value加1 |
void decrement() | 将当前的value减1 |
long sum() | 返回当前值,特别注意,在没有并发更新value的情况下,sum会返回一个精确值,存在并发情况下,sum不保证返回精确值 |
void reset() | 将value重置为0,可用于替代重新new一个LongAdder,但此方法只可以在没有并发更新的情况下 |
long sumThenReset() | 获取当前value,并将value重置为0 |
public class LongAdderAPIDemo {
public static void main(String[] args) {
LongAdder longAdder = new LongAdder();
//1+1
longAdder.increment();
//1+1+1
longAdder.increment();
//1+1+1+1
longAdder.increment();
//1+1+1+1+1
longAdder.increment();
System.out.println(longAdder.sum());
//5代表初始值,第一个参数代表模式
LongAccumulator longAccumulator = new LongAccumulator((x, y) -> x + y, 5);
//1 + 5
longAccumulator.accumulate(1);
//1 + 5 + 4
longAccumulator.accumulate(4);
System.out.println(longAccumulator.get());
}
}
模拟高并发点赞
class ClickNumber {
/**
* 使用重量级锁synchronized
*/
int number = 0;
public synchronized void clickBySynchronized() {
number++;
}
/**
* 使用atomicLong
*/
AtomicLong atomicLong = new AtomicLong(0);
public void clickByAtomicLong() {
atomicLong.getAndIncrement();
}
/**
* longAdder默认从0开始
*/
LongAdder longAdder = new LongAdder();
public void clickByLongAdder() {
// + 1
longAdder.increment();
}
/**
* 规定为x + y
* 从0开始
*/
LongAccumulator longAccumulator = new LongAccumulator((x, y) -> x + y, 0);
public void clickByLongAccumulator() {
longAccumulator.accumulate(1);
}
}
/**
* 50个线程,每个线程100w次,总点赞数出来
*/
public class AccumulatorCompareDemo {
public static final int _1W = 10000;
public static final int threadNumber = 50;
public static void main(String[] args) throws InterruptedException {
ClickNumber clickNumber = new ClickNumber();
CountDownLatch countDownLatch1 = new CountDownLatch(threadNumber);
CountDownLatch countDownLatch2 = new CountDownLatch(threadNumber);
CountDownLatch countDownLatch3 = new CountDownLatch(threadNumber);
CountDownLatch countDownLatch4 = new CountDownLatch(threadNumber);
long starTime, endTime;
starTime = System.currentTimeMillis();
for (int i = 0; i < threadNumber; i++) {
new Thread(() -> {
try {
for (int j = 0; j < 100 * _1W; j++) {
clickNumber.clickBySynchronized();
}
} finally {
countDownLatch1.countDown();
}
}, String.valueOf(i)).start();
}
countDownLatch1.await();
endTime = System.currentTimeMillis();
System.out.println("-----costTime:" + (endTime - starTime) + "毫秒\tclickBySynchronized: " + clickNumber.number);
starTime = System.currentTimeMillis();
for (int i = 0; i < threadNumber; i++) {
new Thread(() -> {
try {
for (int j = 0; j < 100 * _1W; j++) {
clickNumber.clickByAtomicLong();
}
} finally {
countDownLatch2.countDown();
}
}, String.valueOf(i)).start();
}
countDownLatch2.await();
endTime = System.currentTimeMillis();
System.out.println("-----costTime:" + (endTime - starTime) + "毫秒\tclickByAtomicLong: " + clickNumber.atomicLong.get());
starTime = System.currentTimeMillis();
for (int i = 0; i < threadNumber; i++) {
new Thread(() -> {
try {
for (int j = 0; j < 100 * _1W; j++) {
clickNumber.clickByLongAdder();
}
} finally {
countDownLatch3.countDown();
}
}, String.valueOf(i)).start();
}
countDownLatch3.await();
endTime = System.currentTimeMillis();
System.out.println("-----costTime:" + (endTime - starTime) + "毫秒\tclickByLongAdder: " + clickNumber.longAdder.sum());
starTime = System.currentTimeMillis();
for (int i = 0; i < threadNumber; i++) {
new Thread(() -> {
try {
for (int j = 0; j < 100 * _1W; j++) {
clickNumber.clickByLongAccumulator();
}
} finally {
countDownLatch4.countDown();
}
}, String.valueOf(i)).start();
}
countDownLatch4.await();
endTime = System.currentTimeMillis();
System.out.println("-----costTime:" + (endTime - starTime) + "毫秒\tclickBySynchronized: " + clickNumber.longAccumulator.get());
}
}
通过这个案例可以发现,LongAdder的性能更快
2. LongAdder底层原理
为什么LongAdder更快呢?
前面说到AtomicLong是通过CAS实现的,当线程数少的的话,通过自旋锁等待解决
LongAdder的基本思路就是分散热点,将value值分散到一个Cell数组中,不同线程会命中到数组的不同槽中,各个线程只对自己槽中的那个值进行CAS操作,这样热点就被分散了,冲突的概率就小很多。如果要获取真正的long值,只要将各个槽中的变量值累加返回。
sum()会将所有Cell数组中的value和base累加作为返回值,核心的思想就是将之前Atomicl.ong一个value的更新压力分散到多个value中去,从而降级更新热点。
LongAdder的base变量:低并发,直接累加到该变量上
Cell[]数组:高并发,累加进各个线程自己的槽