java.util.concurrent.atomic 分类:
- 基本类型原子类
- AtomicInteger
- AtomicLong
- AtomicBoolean
- 数组类型原子类
- AtomicIntegerArray
- AtomicLongArray
- AtomicReferenceArray
- 引用类型原子类
- AtomicReference
- AtomicMarkableReference
- AtomicStampedReference
- 对象的属性修改原子类
- AtomicIntegerFieldUpdater
- AtomicLongFieldUpdater
- AtomicReferenceFieldUpdater
- 原子操作增强类
- DoubleAccumulator
- DoubleAdder
- LongAccumulator
- LongAdder
AtomicStampedReference 和 AtomicMarkableReference 区别
AtomicStampedReference是携带版本号的引用类型原子类,可以解决ABA问题,记录的是修改过几次
AtomicMarkableReference是将AtomicStampedReference的版本号,简化为true或false,并且只能使用一次
使用示例
-
- 基本类型原子类,以AtomicInteger为例
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicTest {
/**
* 50个线程
*/
private static final int THREAD_NUM = 50;
private static final CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUM);
public static void main(String[] args) throws InterruptedException {
MyNumber myNumber = new MyNumber();
for (int i = 0; i < THREAD_NUM; i++) {
new Thread(() -> {
try {
for (int j = 0; j < 1000; j++) {
myNumber.add();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
}).start();
}
countDownLatch.await();
System.out.println("最终结果:" + myNumber.num.get());
}
}
class MyNumber {
AtomicInteger num = new AtomicInteger();
public void add() {
num.getAndIncrement();
}
}
-
- 数组类型原子类,以AtomicIntegerArray为例
public static void main(String[] args) {
AtomicIntegerArray atomicIntegerArray1 = new AtomicIntegerArray(5);
AtomicIntegerArray atomicIntegerArray2 = new AtomicIntegerArray(new int[5]);
AtomicIntegerArray atomicIntegerArray3 = new AtomicIntegerArray(new int[]{1, 2, 3, 4, 5});
for (int i = 0; i < atomicIntegerArray3.length(); i++) {
// 输出 1、2、3、4、5
System.out.println(atomicIntegerArray3.get(i));
}
int temp = 0;
atomicIntegerArray3.getAndSet(0, 111);
// 输出 111
System.out.println(atomicIntegerArray3.get(0));
// +1
atomicIntegerArray3.getAndIncrement(1);
// +1
atomicIntegerArray3.getAndIncrement(1);
// 输出 4
System.out.println(atomicIntegerArray3.get(1));
}
-
- 引用类型原子类,以AtomicReference为例实现一个自旋锁
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
public class SpinLockDemo {
AtomicReference<Thread> atomicReference = new AtomicReference<>();
public void MyLock() {
System.out.println(Thread.currentThread().getName() + " come in");
while (!atomicReference.compareAndSet(null, Thread.currentThread())) {
}
System.out.println(Thread.currentThread().getName() + " 获取锁成功");
}
public void MyUnLock() {
while (!atomicReference.compareAndSet(Thread.currentThread(), null)) {
}
System.out.println(Thread.currentThread().getName() + " 释放锁");
}
public static void main(String[] args) {
SpinLockDemo spinLockDemo = new SpinLockDemo();
new Thread(() -> {
// 获取锁
spinLockDemo.MyLock();
try {
// 拿到锁使用3秒钟
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 释放锁
spinLockDemo.MyUnLock();
}, "t1").start();
new Thread(() -> {
spinLockDemo.MyLock();
spinLockDemo.MyUnLock();
}, "t2").start();
}
}
输出结果
t1 come in
t1 获取锁成功
t2 come in
t1 释放锁
t2 获取锁成功
t2 释放锁
-
- 对象的属性修改原子类,是以一种线程安全的方式操作非线程安全对象内的某些字段
使用要求:更新的对象属性必须使用 public volatile 进行修饰
AtomicIntegerFieldUpdater使用示例
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
public class AtomicIntegerFieldUpdaterDemo {
public static void main(String[] args) throws InterruptedException {
BankAccount bankAccount = new BankAccount();
for (int i = 0; i < 1000; i++) {
new Thread(() -> {
bankAccount.doAction(bankAccount);
}).start();
}
TimeUnit.SECONDS.sleep(1);
// 输出1000
System.out.println(bankAccount.money);
}
}
class BankAccount {
public String bankName = "中国银行";
public volatile int money;
// 通过反射获取对象
AtomicIntegerFieldUpdater<BankAccount> updater = AtomicIntegerFieldUpdater.newUpdater(BankAccount.class, "money");
public void doAction(BankAccount bankAccount) {
updater.incrementAndGet(bankAccount);
}
}
-
- 原子操作增强类
LongAdder:只能用来计算加法,且从零开始计算
LongAdder longAdder = new LongAdder();
longAdder.increment();
longAdder.increment();
longAdder.increment();
// 输出3
System.out.println(longAdder.longValue());
LongAccumulator:提供了自定义的函数操作
LongAccumulator longAccumulator = new LongAccumulator((x, y) -> x + y, 0);
longAccumulator.accumulate(1);
longAccumulator.accumulate(2);
longAccumulator.accumulate(3);
// 输出6
System.out.println(longAccumulator.longValue());
Atomic高性能对比
示例:使用50个线程,每个线程累加100万次,最后输出结果(类似高并发点赞功能)
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;
class ClickNumber {
int number = 0;
public synchronized void addBySynchronized() {
number++;
}
AtomicInteger atomicInteger = new AtomicInteger(0);
public void addByAtomicInteger() {
atomicInteger.incrementAndGet();
}
AtomicLong atomicLong = new AtomicLong(0);
public void addByAtomicLong() {
atomicLong.incrementAndGet();
}
LongAdder longAdder = new LongAdder();
public void addByLongAdder() {
longAdder.increment();
}
LongAccumulator longAccumulator = new LongAccumulator(Long::sum, 0);
public void addByLongAccumulator() {
longAccumulator.accumulate(1);
}
}
public class LongAdderDemo {
public static void main(String[] args) throws InterruptedException {
ClickNumber clickNumber = new ClickNumber();
long startTime;
long endTime;
CountDownLatch countDownLatch1 = new CountDownLatch(50);
CountDownLatch countDownLatch2 = new CountDownLatch(50);
CountDownLatch countDownLatch3 = new CountDownLatch(50);
CountDownLatch countDownLatch4 = new CountDownLatch(50);
CountDownLatch countDownLatch5 = new CountDownLatch(50);
startTime = System.currentTimeMillis();
// 50个线程
for (int i = 1; i <= 50; i++) {
new Thread(() -> {
try {
for (int j = 1; j <= 1000000; j++) {
// 每个线程累加100万次
clickNumber.addBySynchronized();
}
} finally {
countDownLatch1.countDown();
}
}, String.valueOf(i)).start();
}
countDownLatch1.await();
endTime = System.currentTimeMillis();
System.out.println("synchronized 耗时 " + (endTime - startTime) + " 毫秒 " + clickNumber.number);
startTime = System.currentTimeMillis();
for (int i = 1; i <= 50; i++) {
new Thread(() -> {
try {
for (int j = 1; j <= 1000000; j++) {
clickNumber.addByAtomicInteger();
}
} finally {
countDownLatch2.countDown();
}
}, String.valueOf(i)).start();
}
countDownLatch2.await();
endTime = System.currentTimeMillis();
System.out.println("AtomicInteger 耗时 " + (endTime - startTime) + " 毫秒 " + clickNumber.atomicInteger.get());
startTime = System.currentTimeMillis();
for (int i = 1; i <= 50; i++) {
new Thread(() -> {
try {
for (int j = 1; j <= 1000000; j++) {
clickNumber.addByAtomicLong();
}
} finally {
countDownLatch3.countDown();
}
}, String.valueOf(i)).start();
}
countDownLatch3.await();
endTime = System.currentTimeMillis();
System.out.println("AtomicLong 耗时 " + (endTime - startTime) + " 毫秒 " + clickNumber.atomicLong.get());
startTime = System.currentTimeMillis();
for (int i = 1; i <= 50; i++) {
new Thread(() -> {
try {
for (int j = 1; j <= 1000000; j++) {
clickNumber.addByLongAdder();
}
} finally {
countDownLatch4.countDown();
}
}, String.valueOf(i)).start();
}
countDownLatch4.await();
endTime = System.currentTimeMillis();
System.out.println("LongAdder 耗时 " + (endTime - startTime) + " 毫秒 " + clickNumber.longAdder.longValue());
startTime = System.currentTimeMillis();
for (int i = 1; i <= 50; i++) {
new Thread(() -> {
try {
for (int j = 1; j <= 1000000; j++) {
clickNumber.addByLongAccumulator();
}
} finally {
countDownLatch5.countDown();
}
}, String.valueOf(i)).start();
}
countDownLatch5.await();
endTime = System.currentTimeMillis();
System.out.println("LongAccumulator 耗时 " + (endTime - startTime) + " 毫秒 " + clickNumber.longAccumulator.longValue());
}
}
从输出结果可见,LongAdder性能明显高于其它的
synchronized 耗时 941 毫秒 50000000
AtomicInteger 耗时 980 毫秒 50000000
AtomicLong 耗时 984 毫秒 50000000
LongAdder 耗时 153 毫秒 50000000
LongAccumulator 耗时 262 毫秒 50000000
LongAdder为什么比AtomicLong快?
-
官方文档上的解释是,在线程数竞争较少的情况下,AtomicLong和LongAdder类似,但是在更高并发情况下,LongAdder的吞吐量明显更高,但是代价是更高的空间消耗。这也就是空间换时间
-
AtomicInteger 和 AtomicLong 父类是 Number 。而 LongAdder 父类是 Striped64
public class AtomicLong extends Number
public class LongAdder extends Striped64
abstract class Striped64 extends Number
在 Striped64 源码中,有如下定义
/**
* Number of CPUS, to place bound on table size
* CPU 核心数量
*/
static final int NCPU = Runtime.getRuntime().availableProcessors();
/**
* Table of cells. When non-null, size is a power of 2.
* Cell 数组,为2的幂,2,4,8,16.....方便位运算,上限就是CPU核心数量
*/
transient volatile Cell[] cells;
/**
* Base value, used mainly when there is no contention, but also as
* a fallback during table initialization races. Updated via CAS.
* 基础value值,当并发较低时,只累加该值主要用于没有竞争的情况,通过CAS更新
*/
transient volatile long base;
/**
* Spinlock (locked via CAS) used when resizing and/or creating Cells.
* 创建或者扩容cells数组时使用的自旋锁变量调整单元格大小(扩容),创建单元格时使用的锁
*/
transient volatile int cellsBusy;
LongAdder 的基本思路就是分散热点,将value值分散到一个 Cell 数组中,不同线程会命中到数组的不同槽中,各个线程只对自己槽中的那个值进行 CAS 操作,这样热点就被分散了,冲突的概率就小了很多。如果要获取真正的 long 值,只要将各个槽中的变量值累加返回。
sun() 会将所有 Cell 数组中的 value 和 base 累积作为返回值,核心的思想是将之前 AtomicLong 一个 value 的更新压力分散到多个 value 中去,从而降低更新热点。
sun求和公式:
LongAdder 缺陷:sum求和后还有其它线程修改了结果的话,最后结果不够准确
LongAdder 在无竞争的情况下,跟 AtomicLong一样,对同一个 base 进行操作,当出现竞争关系时则是采用化整为零的做法,用空间换时间,用一个数组cells,将一个value拆分进这个数组cells。
多个线程需要同时对 value 进行操作的时候,可以对线程id进行hash得到hash值,再根据hash值映射到这个数组cells的某个下标,再对该下标所对应的值进行自增操作。
当所有线程操作完毕,将数组cells的所有值和无竞争值base都加起来作为最终结果。