java多线程系列5 atomic简介

时间:2023-03-09 09:31:00
java多线程系列5 atomic简介

先看一个例子,AtomicInteger 实现的线程安全的累加器

public class AtomicIntTest {
public static void main(String[] args) {
AddRunnable addRunnable = new AddRunnable();
Thread myThread1 = new Thread(addRunnable);
Thread myThread2 = new Thread(addRunnable);
myThread1.start();
myThread2.start();
try {
myThread1.join();
myThread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println(addRunnable.count);
} } class AddRunnable implements Runnable {
AtomicInteger count = new AtomicInteger(); @Override
public void run() {
for (int i = 0; i < 100000; i++) {
count.incrementAndGet();
}
} }

AtomicInteger源码分析

下面通过AtomicInteger的源码来看一下是怎么在没有锁的情况下保证数据正确性。首先看一下incrementAndGet方法的实现

public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}

public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

  注意::valueOffset叫做 偏移量 我们知道valueOffset指向的地址对应的值就是原始变量的值 执行逻辑就是 expect 和valueOffset 比较 相同 就替换成 update 不同继续循环

 

 总结:

1 虽然基于CAS的线程安全机制很好很高效,但要说的是,并非所有线程安全都可以用这样的方法来实现,这只适合一些粒度比较小,型如计数器这样的需求用起来才有效,否则也不会有锁的存在了。实际来发中,用锁的情况还是较多。

2 并发越高,失败的次数会越多,CAS如果长时间不成功,会极大的增加CPU的开销。(毕竟死循环嘛) 因此CAS不适合竞争十分频繁的场景。

3 CAS只能保证一个共享变量的原子操作。当对多个共享变量操作时,CAS就无法保证操作的原子性,这时就可以用锁,或者把多个共享变量合并成一个共享变量来操作。使用AtomicReference。