atomicReference 可以保证对象的原子操作.
public static void main(String[] args) { AtomicReference<Simple> atomic = new AtomicReference<>(new Simple("xiaodao",23)); System.out.println(atomic.get()); boolean result = atomic.compareAndSet(atomic.get(), new Simple("bbb", 90)); System.out.println(result); } static class Simple{ private String name; private int age; public Simple(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
CAS
cas带来的好处
1.可见性
2.有序性
3.原子性
volatile修饰的变量,保证前俩者
CAS算法,也就是cpu级别的同步指令,相当于乐观锁,它可以探测到其他线程对共享数据的变化情况
cas带来一个ABA问题
什么是ABA呢?
就是俩个线程同事操作,有可能有一个线程已经处理结束,那么第一个线程中间又一次fail操作,会在这个结果上在进行计算
t1 t2
A A->B->A
A->C
那我们来看下维基百科上面的形象描述:
你拿着一个装满钱的手提箱在飞机场,此时过来了一个火辣性感的美女,然后她很暖昧地挑逗着你,并趁你不注意的时候,把用一个一模一样的手提箱和你那装满钱的箱子调了个包,然后就离开了,你看到你的手提箱还在那,于是就提着手提箱去赶飞机去了。
如何解决ABA的问题呢?
在数据库中是使用乐观锁来解决的这问题version一直在变化
在多线程中我们有一个类 AtomicStampedReference可以这样解决
public class AtomicStampedReferenceTest { private static AtomicStampedReference<Integer> atomic = new AtomicStampedReference<>(100,0); public static void main(String[] args) { new Thread(()->{ try { TimeUnit.SECONDS.sleep(1); //和乐观相似,第一个stamp相当于 version 每次加1 如果别的线程修改过就是1了.第一个线程0就对比不成功 boolean sucess = atomic.compareAndSet(100,101,atomic.getStamp(),atomic.getStamp()+1); System.out.println(sucess); sucess = atomic.compareAndSet(101,100,atomic.getStamp(),atomic.getStamp()+1); System.out.println(sucess); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); new Thread(()->{ try { int stamp = atomic.getStamp(); System.out.println("before: "+stamp);//这里获取到的stamp是1 TimeUnit.SECONDS.sleep(2); int stamp1 = atomic.getStamp(); System.out.println("第一个线程的 stamp "+ stamp1);//这里第一个线程的stamp已经变成2 了 //和乐观相似,第一个stamp相当于 version 每次加1 如果别的线程修改过就是1了.第一个线程0就对比不成功 boolean b = atomic.compareAndSet(100, 101, stamp, stamp + 1); System.out.println("2线程= "+b); atomic.compareAndSet(101,100,stamp,stamp+1); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } } }
源码:
我=copy了一点 也不算太复杂,就是初始化的时候放进入一个值,和一个版本号,
然后进行compareAndSet 的话,就是比对预期的值和当前的值,还有预期的版本号和当前的版本号
private static class Pair<T> { final T reference; final int stamp; private Pair(T reference, int stamp) { this.reference = reference; this.stamp = stamp; } static <T> Pair<T> of(T reference, int stamp) { return new Pair<T>(reference, stamp); } } private volatile Pair<V> pair; /** * Creates a new {@code AtomicStampedReference} with the given * initial values. * * @param initialRef the initial reference * @param initialStamp the initial stamp */ public AtomicStampedReference(V initialRef, int initialStamp) { pair = Pair.of(initialRef, initialStamp); }
public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
Pair<V> current = pair;
return
expectedReference == current.reference && expectedStamp == current.stamp && ((newReference == current.reference && newStamp == current.stamp) ||
casPair(current, Pair.of(newReference, newStamp)));
}