建设网站素材,设计狮官网,一重大连工程建设有限公司官方网站,企装网怎么样CAS #xff08;Compare And Set#xff09;比较并替换
上篇文章的锁问题解决#xff0c;可以使用更高效的方法#xff0c;使用AtomXXX类#xff0c;AtomXXX类本身方法都是原子性的#xff0c;但不能保证多个方法连续调用是原于性的。
import java.util.ArrayList;
imp…CAS Compare And Set比较并替换
上篇文章的锁问题解决可以使用更高效的方法使用AtomXXX类AtomXXX类本身方法都是原子性的但不能保证多个方法连续调用是原于性的。
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;public class AtomicIntegerTest {AtomicInteger count new AtomicInteger(0);void m1 (){for(int i0;i10000;i){count.incrementAndGet(); // 相当于线程安全的 count}}public static void main(String[] args) {AtomicIntegerTest t new AtomicIntegerTest();ListThread Threads new ArrayList();for(int i0;i10;i){Threads.add(new Thread(t::m1,Thread-i));}Threads.forEach((o) - o.start());Threads.forEach((o) - {try {o.join();} catch (InterruptedException e) {e.printStackTrace();}});System.out.println(t.count);}
}
结果 100000 那为什么 count.incrementAndGet(); 是线程安全的 点进去看它的源码可知 底层使用了 weakCompareAndSetInt(o, offset, v, v delta) 方法这个方法就是 CAS。
CAS 概念
cas方法一共有三个参数 V要改的值内存位置 Expected期望值预期原值 NewValue要设定的新值
如果 要改的值 v 和 expected 的值是一样的则将 v的值改为新值 newValue。如果不是一样的则循环下一次比较或直接返回失败。
注因为 cas方法是 CPU原语支持的即 在比较替换的过程中是不可以被打断的所以不会出现 比较成功的时候其他线程将要改的值或者新值替换的问题。
ABA问题
aba问题是在进行比较并替换的时候将a值改为b然后马上将b再改回a这样的话比较是可以成功的但是对于原值a它的版本已经不是原始的了。
如果这个a的值是基础类型则没什么关系。但是如果是Object类型比如a引用 bb引用c这是一个线程将 a引用到了c并将c的一些属性做了修改再将a引用到b。这个时候业务执行的一些逻辑会导致各种问题的出现。如果还没懂引入一个经典的例子
一个小偷把别人家的钱偷了之后又还了回来还是原来的钱吗你老婆出轨之后又回来还是原来的老婆嘛ABA问题也一样如果不好好解决就会带来大量的问题。最常见的就是资金问题也就是别人如果挪用了你的钱在你发现之前又还了回来。但是别人却已经触犯了法律。
那怎么解决呐
案例重现 private static AtomicInteger index new AtomicInteger(10);public static void main(String[] args) {new Thread(() - {index.compareAndSet(10, 11);index.compareAndSet(11, 10);System.out.println(Thread.currentThread().getName() 10-11-10);},张三).start();new Thread(() - {try {TimeUnit.SECONDS.sleep(2);boolean isSuccess index.compareAndSet(10, 12);System.out.println(Thread.currentThread().getName() index是否为预期值10isSuccess 设置的新值是index.get());} catch (InterruptedException e) {e.printStackTrace();}},李四).start();} 张三 10-11-10 李四 index是否为预期值10true 设置的新值是12 通过AtomicStampedReference 添加版本号 解决这个问题 private static AtomicInteger index new AtomicInteger(10);static AtomicStampedReferenceInteger stampRef new AtomicStampedReference(10, 1);public static void main(String[] args) {new Thread(() - {int stamp stampRef.getStamp();System.out.println(Thread.currentThread().getName() 第1次版本号 stamp);stampRef.compareAndSet(10, 11,stampRef.getStamp(),stampRef.getStamp()1);System.out.println(Thread.currentThread().getName() 第2次版本号 stampRef.getStamp());stampRef.compareAndSet(11, 10,stampRef.getStamp(),stampRef.getStamp()1);System.out.println(Thread.currentThread().getName() 第3次版本号 stampRef.getStamp());},张三).start();new Thread(() - {try {int stamp stampRef.getStamp();System.out.println(Thread.currentThread().getName() 第1次版本号 stamp);TimeUnit.SECONDS.sleep(2);boolean isSuccess stampRef.compareAndSet(10, 12,stampRef.getStamp(),stampRef.getStamp()1);System.out.println(Thread.currentThread().getName() 修改是否成功 isSuccess 当前版本 stampRef.getStamp());System.out.println(Thread.currentThread().getName() 当前实际值 stampRef.getReference());} catch (InterruptedException e) {e.printStackTrace();}},李四).start();} 张三 第1次版本号 1 李四 第1次版本号 1 张三 第2次版本号 2 张三 第3次版本号 3 李四 修改是否成功 true 当前版本 4 李四 当前实际值 12 这里使用的是AtomicStampedReference的compareAndSet函数这里面有四个参数
compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp)。
1第一个参数expectedReference表示预期值。
2第二个参数newReference表示要更新的值。
3第三个参数expectedStamp表示预期的时间戳。
4第四个参数newStamp表示要更新的时间戳。
重现案例即解决方法原文地址解决CAS机制中ABA问题的AtomicStampedReference详解 - 知乎 (zhihu.com) 总结所有以 AtomXXX开头的类底层都是使用cas方法并是通过 Unsafe类实现的。
Unsafe类的出现等于c/c的指针给 java语言赋予了 原来 C/C实现的指针方法。比如
allocateMemory 、freeMemory等操作内存的方法。补充Atomic、Sync、LongAdder的比较
耗时比较Sync Atomic LongAdder
线程数大、循环数大的情况下使用 LongAdder优势很明显。LongAdder底层使用的是分段锁cas方式。将大量线程分段执行最后相加。
线程数小、循环数小的情况下用Atomic