AtomicInteger类相关
CAS --> UnSafe --> CAS底层思想 --> ABA问题 --> 原子引用更新 --> 如何规避ABA问题
ABA问题是如何产生的
CAS会导致“ABA问题”
CAS算法实现一个重要的前提时需要取出内存中某个时刻的数据并在当下时刻比较并替换,那么在这个时间差内有可能会出现数据变化的。
例如:
线程1从内存位置V 中取出了A,这是另一个线程2也从内存中取出A,并且线程2进行了一些操作将值修改为了B,然后线程2又将其改回了A,这时候线程1,进行CAS操作时是无法察觉其中的变化的,取出仍为A,CAS通过,线程1 执行操作。
线程1:--取值为A----------------------------------------取值为A,CAS通过--------
线程2:--取值为A--------改值为B--------该值为A-------------------------------------
原子引用
JUC除了提供了原子整形AtomicInteger
,原子布尔型AtomicBoolean
等等,还提供了原子引用AtomicReference
- AtomicBoolean
- AtomicInteger
- AtomicIntegerArray
- AtomicIntegerFieldUpdater
- AtomicLong
- AtomicLongArray
- AtomicLongFieldUpdater
- AtomicMarkableReference
- AtomicReference
- AtomicReferenceArray
- AtomicReferenceFieldUpdater
- AtomicStampedReference
- DoubleAccumulator
- DoubleAdder
- LongAccumulator
- LongAdder
package com.company;
import java.util.concurrent.atomic.AtomicReference;
class User{
String userName;
int age;
User(String userName,int age){
this.userName = userName;
this.age = age;
}
public String toString(){
return this.userName;
}
}
public class AtomicReferenceDemo {
public static void main(String[] args) {
User U1 = new User("Zhang3",20);
User U2 = new User("Li4",25);
AtomicReference atomicReference_User = new AtomicReference<>();
atomicReference_User.set(U1);
//true
System.out.println(atomicReference_User.compareAndSet(U1,U2)
+"\t"+atomicReference_User.get().toString());
//false
System.out.println(atomicReference_User.compareAndSet(U1,U2)
+"\t"+atomicReference_User.get().toString());
}
}
时间戳原子引用——ABA问题的解决
理解原子引用+新增一种机制,那就是修改版本号(类似于时间戳)
线程1:--取值为A,1----------------------------------------取值为A,3,CAS不通过--------
线程2:--取值为A,1--------改值为B,2--------该值为A,3-------------------------------------
乐观锁:我认为别人应该不会在我之前进行改动
java.util.concurrent.atomic.AtomicStampedReference
一个AtomicStampedReference维护对象引用以及整数“印记”,可以原子更新。
实现注意事项:此实现通过创建表示“boxed”[引用,整数]对的内部对象来维护加盖引用。
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;
/*
*ABA问题的解决 --> AtomicStampedReference
*/
public class ABADemo
{
static AtomicReference atomicReference = new AtomicReference(100);
static AtomicStampedReference atomicStampedReference = new AtomicStampedReference(100,1);
public static void main(String[] args)
{
System.out.println("-------------------ABA问题产生-------------------");
new Thread(()->{
atomicReference.compareAndSet(100,101);//A->B
atomicReference.compareAndSet(101,100);//B->A
},"t1").start();
new Thread(()->{
//t2暂停一秒钟,保证ABA结束
try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}
//true
System.out.println("t2的修改结果: \t"+atomicReference.compareAndSet(100,1048) +"\t"+ atomicReference.get());
},"t2").start();
try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}
System.out.println("-------------------ABA问题解决-------------------");
new Thread(()->{
int stamp = atomicStampedReference.getStamp();
//等待t4获取到相同的版本号
try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}
atomicStampedReference.compareAndSet(100,101,1,2);
System.out.println("t3的修改结果: \treference:"+atomicStampedReference.getReference()+"\t"+"stamp:"+atomicStampedReference.getStamp());
atomicStampedReference.compareAndSet(101,100,2,3);
System.out.println("t3的修改结果: \treference:"+atomicStampedReference.getReference()+"\t"+"stamp:"+atomicStampedReference.getStamp());
},"t3").start();
new Thread(()->{
int stamp = atomicStampedReference.getStamp();
//等待t3完成ABA
try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}
System.out.println("t4的修改结果: \t"+atomicStampedReference.compareAndSet(100,1024,1,2));
System.out.println("最终结果: \t\treference:"+atomicStampedReference.getReference()+"\t"+"stamp:"+atomicStampedReference.getStamp());
},"t4").start();
}
}
控制台输出:
-------------------ABA问题产生-------------------
t2的修改结果: true 1048
-------------------ABA问题解决-------------------
t3的修改结果: reference:101 stamp:2
t3的修改结果: reference:100 stamp:3
t4的修改结果: false
最终结果: reference:100 stamp:3
Process finished with exit code 0
小结
带时间戳(版本号)的原子引用,在除了