CAS
介绍
比较当前工作内存中的值和主内存的值,若这个值为期望值,则执行操作。若不是就一直循环,即阻塞。
public final boolean compareAndSet(int expect, int update)
public final boolean compareAndSet(int expect, int update)
是一个AtomicInteger的实例方法。
若实例的值为expect,则赋值为update。
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Demo { public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(123); System.out.println("1. "+atomicInteger); System.out.println(atomicInteger.compareAndSet(123, 345)); System.out.println("2. "+atomicInteger); System.out.println(atomicInteger.compareAndSet(234,123)); System.out.println("3. "+atomicInteger); } }
|
1 2 3 4 5 6 7
| 1. 123 true 2. 345 false 3. 345
Process finished with exit code 0
|
atomicInteger的初始值为123,经过第一次操作,123变为345,方法返回值为true,表示成功。
后来atomicInteger的值为345,第二次操作,若值为234则变为123,显然不是345,返回false,修改失败。
值仍为345。
剖析底层的CAS
getAndIncrement()解析
1 2 3
| public final int getAndIncrement() { return unsafe.getAndAddInt(this, valueOffset, 1); }
|
方法getAndIncrement()可以为AtomicInteger对象通过内存方式实现“++”,其中,getAndIncrement()方法调用了getAndAddInt()方法,其中,为this即为当前对象,valueOffset为”对象为内存中的偏移值”(详见“其他”章节Unsafe类),“1”为增加的大小。
getAndAddInt()解析
![image]()
CAS总结
CAS:比较当前工作内存中的值和主内存的值,若这个值为期望值,则执行操作。若不是就一直循环,即阻塞。
缺点:
- 循环耗时;
- 一次性只能保证一个共享变量的原子性;
- ABA问题。
CAS: ABA问题(狸猫换太子)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class Demo33_ABA问题 { public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(123);
System.out.println(atomicInteger.compareAndSet(123,321)); System.out.println(atomicInteger); System.out.println(atomicInteger.compareAndSet(321,123)); System.out.println(atomicInteger); System.out.println(atomicInteger.compareAndSet(123,321)); System.out.println(atomicInteger);
} }
|
原子引用
AtomicStampedReference
可以原子更新的对象引用。 思想:乐观锁
AtomicStampedReference介绍
AtomicStampedReference创建的对象,有功能:
- 指向一个泛型对象;
- 记录一个stamp值(类似版本号)。
通过compareAndSet()方法可以修改AtomicStampedReference对象的指向。在使用该方法更新指向时,可以指定原对象、修改后对象、期望stamp值、目标stamp值。
该方法会判断指定的“原对象”和“期望stamp值”是否与存储的一致,
- 若一致,则修改指向和stamp值,返回true;
- 若不一致,则不修改,返回false。
在线程执行前,记录对象的stamp值,然后进行业务代码。在这期间,若对象被修改,会留下stamp更新。再次修改对象前,核对stamp值,可以得知对象是否在这期间修改过。
例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| public class Demo34_AtomicReference { public static void main(String[] args) { AtomicStampedReference<Integer> atomicReference = new AtomicStampedReference<>(12,1); new Thread(()->{ System.out.println(Thread.currentThread().getName()+"1-> "+atomicReference.getStamp()); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("A "+atomicReference.compareAndSet( 12, 123, atomicReference.getStamp(), atomicReference.getStamp() + 1 )); System.out.println(Thread.currentThread().getName()+"2-> "+atomicReference.getStamp());
System.out.println("A "+atomicReference.compareAndSet( 123, 12, atomicReference.getStamp(), atomicReference.getStamp() + 1 ));
System.out.println(Thread.currentThread().getName()+"3-> "+atomicReference.getStamp()); },"A").start(); new Thread(()->{ int stamp = atomicReference.getStamp(); System.out.println(Thread.currentThread().getName()+"1-> "+stamp); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("B "+atomicReference.compareAndSet( 12, 23, stamp, stamp + 1 )); System.out.println(Thread.currentThread().getName()+"2-> "+atomicReference.getStamp()); },"B").start(); } }
|
其他
Unsafe类
Unsafe用于让Java操作内存。
原理:Java不能操作内存,但C++可以操作内存。Java有个Unsafe类有native本地方法,可以调用C++操作内存。
![image]()
int的包装类Interger
Integer使用了对象缓存机制,默认范围 -128 ~ 127。
推荐使用静态工厂方法 valueOf 获取对象实例,而不是new,因为valueOf使用缓存,而new一定会创建新的对象分配新的内存空间。
对于Integer var = ?在**-128 ~ 127之间的赋值,Integer对象是在IntegerCache.cache产生,会复用已有对象,这个区间内的Integer值可以直接使用“==”进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象。推荐使用equals**方法进行判断。