时间:2021-07-01 10:21:17 帮助过:18人阅读
1 private volatile int value; 2 3 public final int get() { 4 return value; 5 } 6 7 public final int getAndIncrement() { 8 for (;;) { 9 int current = get(); 10 int next = current + 1; 11 if (compareAndSet(current, next)) 12 return current; 13 } 14 } 15 16 public final boolean compareAndSet(int expect, int update) { 17 return unsafe.compareAndSwapInt(this, valueOffset, expect, update); 18 } 19 }
在没有锁的机制下需要字段value要借助volatile原语,保证线程间的数据是可见的。这样在获取变量的值的时候才能直接读取。然后来看看++i
是怎么做到的。
getAndIncrement
采用了CAS操作,每次从内存中读取数据然后将此数据和+1
后的结果进行CAS操作,如果成功就返回结果,否则重试直到成功为止。而compareAndSet
利用JNI来完成CPU指令的操作。
CAS会导致“ABA问题”。
CAS算法实现一个重要前提需要取出内存中某时刻的数据,而在下时刻比较并替换,那么在这个时间差类会导致数据的变化。
比如说一个线程one从内存位置V中取出A,这时候另一个线程two也从内存中取出A,并且two进行了一些操作变成了B,然后two又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,然后one操作成功。尽管线程one的CAS操作成功,但是不代表这个过程就是没有问题的。
部分乐观锁的实现是通过版本号(version
)的方式来解决ABA问题,乐观锁每次在执行数据的修改操作时,都会带上一个版本号,一旦版本号和数据的版本号一致就可以执行修改操作并对版本号执行+1
操作,否则就执行失败。因为每次操作的版本号都会随之增加,所以不会出现ABA问题,因为版本号只会增加不会减少。
Java中的线程安全问题至关重要,要想保证线程安全,就需要锁机制。锁机制包含两种:乐观锁与悲观锁。悲观锁是独占锁,阻塞锁。乐观锁是非独占锁,非阻塞锁。有一种乐观锁的实现方式就是CAS ,这种算法在JDK 1.5中引入的java.util.concurrent
中有广泛应用。但是值得注意的是这种算法会存在ABA问题。
另外,CAS还有一个应用,那就是在JVM创建对象的过程中。对象创建在虚拟机中是非常频繁的。即使是仅仅修改一个指针所指向的位置,在并发情况下也不是线程安全的,可能正在给对象A分配内存空间,指针还没来得及修改,对象B又同时使用了原来的指针来分配内存的情况。解决这个问题的方案有两种,其中一种就是采用CAS配上失败重试的方式保证更新操作的原子性。
非阻塞同步算法与CAS(Compare and Swap)无锁算法
CAS原理分析
Java CAS 和ABA问题
[数据库事务与锁]详解八:底理解数据库事务乐观锁的一种实现方式——CAS
标签:方法 失败 信息 ber 变量 算法 重试 顺序 应该