场景
比如一个int类型的变量,多线程下去操作这个变量,这时候吧比加锁更好的解决方法就是atomicInteger
另外AtomicReference主要是用在了自旋锁
automic原理-CAS
CAS是指Compare And Swap,比较并交换,是一种很重要的同步思想。如果主内存的值跟期望值一样,那么就进行修改,否则一直重试,直到一致为止。
我们在看原子性的时候用了AtomicInteger来实现了和加锁sync一样的效果
查看AtomicInteger.getAndIncrement()
方法,发现源码里根本没有用到synchronized
也实现了同步。这是为什么?
JMM
CAS底层原理
- volatile int value保证线程间的可见性
- 利用UnSafe类去操作底层操作系统,UnSafe类都是native方法
- valueOffset来记录变量在内存的偏移地址
//源码片段
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
...
private volatile int value;
...
public final int getAndIncrement(){
return unsafe.getAndAddInt(this,valueOffset,1);
}
public final int getAnddAddInt(Object var1,long var2,int var4){
int var5;
//在这用了一个循环,不停的去比较
do{
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
//源码片段
public final class Unsafe {
...
public native void putByte(Object var1, long var2, byte var4);
public native short getShort(Object var1, long var2);
...
}
比如主内存的变量V,T1线程本地内存拿到的时候是A,把他修改为B
如果中间线程T2修改了V,由于加了volatile,所以这个修改是立即可见的
A线程发现这个时候主内存的值不等于快照值
所以继续循环,重新从主内存获取
CAS缺点
CAS实际上是一种自旋锁,
- 一直循环,开销比较大,上面源码里可以看到是while循环
- 只能保证一个变量的原子操作,多个变量依然要加锁。
- 引出了ABA问题。
ABA问题
首先我们可以看到CAS原理只注重头和尾,只要首尾一致就接受。但是有的需求,还看重过程,中间不能发生任何修改,这就引出了AtomicReference
原子引用
public class ABADemo {
static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);
public static void main(String[] args) {
System.out.println("======ABA问题的产生======");
new Thread(() -> {
//atomicReference.compareAndSet(100, 101) atomicReference是100就返回true并改为101,不是就返回false什么都不做
//我先改成101,再改成100
System.out.println(atomicReference.compareAndSet(100, 101) + " " + atomicReference.get().toString());
System.out.println(atomicReference.compareAndSet(101, 100) + " " + atomicReference.get().toString());
}, "t1").start();
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
//我们这里其实只看他是不是100,你中间做了什么我不管,虽然atomicReference中途被改成了101
System.out.println(atomicReference.compareAndSet(100, 2019) + " " + atomicReference.get().toString());
}, "t2").start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
======ABA问题的产生======
true 101
true 100
true 2019
AtomicStampedReference解决ABA
使用AtomicStampedReference
类可以解决ABA问题。这个类维护了一个“版本号”Stamp,在进行CAS操作的时候,不仅要比较当前值,还要比较版本号。只有两者都相等,才执行更新操作
public class ABADemo {
static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);
public static void main(String[] args) {
System.out.println("======ABA问题的解决======");
new Thread(() -> {
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + " stamp: " + stamp);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicStampedReference.compareAndSet(100, 101,
atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
System.out.println(Thread.currentThread().getName() + " stamp: " + atomicStampedReference.getStamp());
atomicStampedReference.compareAndSet(101, 100,
atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
System.out.println(Thread.currentThread().getName() + " stamp: " + atomicStampedReference.getStamp());
}, "t3").start();
new Thread(() -> {
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + " stamp: " + stamp);
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean result = atomicStampedReference.compareAndSet(100, 2019,
stamp, stamp + 1);
System.out.println(Thread.currentThread().getName() + " " + result + " stamp" + atomicStampedReference.getStamp());
System.out.println(Thread.currentThread().getName() + " value:" + atomicStampedReference.getReference());
}, "t4").start();
}
}
======ABA问题的解决======
t3 stamp: 1
t4 stamp: 1
t3 stamp: 2
t3 stamp: 3
t4 false stamp3
t4 value:100