java.util.concurrent包系列文章
JUC—ThreadLocal源码解析(JDK13)
JUC—ThreadPoolExecutor线程池源码解析(JDK13)
JUC—各种锁(JDK13)
JUC—原子类Atomic*.java源码解析(JDK13)
JUC—CAS源码解析(JDK13)
JUC—ConcurrentHashMap源码解析(JDK13)
JUC—CopyOnWriteArrayList源码解析(JDK13)
JUC—并发队列源码解析(JDK13)
JUC—多线程下控制并发流程(JDK13)
JUC—AbstractQueuedSynchronizer解析(JDK13)
一、什么是原子类
- 不可分割性
- 一个操作是不可中断的,在多线程下也可以保证
- 相比于锁,原子变量可以把竞争资源缩小到变量级别,粒度更细。
- 通常情况下比锁的效率更高,但是在高度竞争的情况下性能更低。
以AtomicInteger为例,看看它的方法
int get();//获取到当前值
int getAndSet(int newValue) {//获取到当前值并设置新的值
return U.getAndSetInt(this, VALUE, newValue);
}
int getAndIncrement() {//获取到当前值并自增
return U.getAndAddInt(this, VALUE, 1);
}
int getAndDecrement() {//获取到当前值并自减
return U.getAndAddInt(this, VALUE, -1);
}
int getAndAdd(int delta) {//获取到当前值并增加delta
return U.getAndAddInt(this, VALUE, delta);
}
//最重要的CAS操作:如果当前的值是expectedValue,则设置当前值为newValue
boolean compareAndSet(int expectedValue, int newValue) {
return U.compareAndSetInt(this, VALUE, expectedValue, newValue);
}
为什么是线程安全的:
public final int getAndIncrement() {
return U.getAndAddInt(this, VALUE, 1);
}
//getAndIncrement方法就是调用了Unsafe类的getAndAddInt方法,并且通过do while循环自旋CAS。从而保证原子性。
@HotSpotIntrinsicCandidate
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset);
} while (!weakCompareAndSetInt(o, offset, v, v + delta));
return v;
}
常用的原子类和原子数组的实例代码Demo在我的GitHub上:源码
二、原子引用类AtomicReference
AtomicReference的作用:和AtomicInteger效果一样,AtomicInteger可以让一个整数原子性,AtomicReference可以让一个对象保证原子性。
看看源码,也是CAS,熟悉的味道:
利用AtomicReference实现自旋锁:
public class SpinLock {
private AtomicReference<Thread> sign = new AtomicReference<>();
public void lock() {
Thread current = Thread.currentThread();
// 当前没有线程获取到锁的情况下才会跳出while循环,自旋操作
while (!sign.compareAndSet(null, current)) {
System.out.println("自旋获取失败,再次尝试");
}
}
public void unlock() {
Thread current = Thread.currentThread();
// 释放当前线程的锁
sign.compareAndSet(current, null);
}
public static void main(String[] args) {
SpinLock spinLock = new SpinLock();
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "开始尝试获取自旋锁");
spinLock.lock();
System.out.println(Thread.currentThread().getName() + "获取到了自旋锁");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
spinLock.unlock();
System.out.println(Thread.currentThread().getName() + "释放了自旋锁");
}
}
};
Thread thread1 = new Thread(runnable);
Thread thread2 = new Thread(runnable);
thread1.start();
thread2.start();
}
}
三、AtomicIntegerFieldUpdater把普通变量升级为原子变量
public class AtomicIntegerFieldUpdaterDemo implements Runnable{
static Candidate tom;
static Candidate peter;
public static AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater
.newUpdater(Candidate.class, "score");
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
peter.score++;
scoreUpdater.getAndIncrement(tom);
}
}
public static class Candidate {
volatile int score;
}
public static void main(String[] args) throws InterruptedException {
tom=new Candidate();
peter=new Candidate();
AtomicIntegerFieldUpdaterDemo r = new AtomicIntegerFieldUpdaterDemo();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("普通变量:"+peter.score);
System.out.println("升级后的结果"+ tom.score);
}
}
四、LongAdder累加器
- 高并发下LongAdder比AtomicLong效率高,不过本质是空间换时间
- 竞争激烈的时候,LongAdder把不同线程对应到不同cell上进行修改,降低了冲突概率,利用了多段锁的概念。提高了并发。
AtomicLong为什么更慢:
因为它每一次操作都需要flush和refresh。每一次修改了值都需要把当前值从线程缓存刷到主内存,再同步到每一个线程的内存。在core1中修改了值,需要把值从local cache flush到共享内存(主内存),再从主内存refresh到每个线程的local cache。
LongAdder的改进:
竞争激烈的情况下,每个线程在自己的内存中计算,互相不干扰,也不需要同步每一次修改过后的值。只需要在计算结束后把每个线程的结果加起来。LongAdder采用分段累加,内部有一个base变量,和一个Cell[]数组。
- 竞争不激烈的情况直接累加到base变量
- 竞争激烈的情况,各个线程分散累加到自己的cell[i]槽中
其本质就是空间换时间,
public void add(long x) {
Cell[] cs; long b, v; int m; Cell c;
// 先尝试casBase
if ((cs = cells) != null || !casBase(b = base, b + x)) {
boolean uncontended = true;
if (cs == null || (m = cs.length - 1) < 0 ||
(c = cs[getProbe() & m]) == null ||//当前线程分配的cell[]槽
!(uncontended = c.cas(v = c.value, v + x)))
longAccumulate(x, null, uncontended);
}
}
public long sum() {
Cell[] cs = cells;
long sum = base;
if (cs != null) {
for (Cell c : cs)
if (c != null)
sum += c.value;
}
return sum;
}
五、Accumulator累加器
适用于大量计算、并行计算的场景。
public class LongAccumulatorDemo {
public static void main(String[] args) {
LongAccumulator accumulator = new LongAccumulator((x, y) -> 2 + x * y, 1);
//LongAccumulator accumulator = new LongAccumulator((x, y) -> Math.max(x, y), 1);
ExecutorService executor = Executors.newFixedThreadPool(8);
IntStream.range(1, 10).forEach(i -> executor.submit(() -> accumulator.accumulate(i)));
executor.shutdown();
while (!executor.isTerminated()) {
}
System.out.println(accumulator.getThenReset());
}
}