暮年|20年架构师深入讲解java多线程与高并发:volatile与CAS,涨薪5K( 六 )

< 10000; i++)//if count1.get() < 1000count.incrementAndGet(); //count1++}public static void main(String[] args) {T01_AtomicInteger t = new T01_AtomicInteger();List threads = new ArrayList();for (int i = 0; i < 10; i++) {threads.add(new Thread(t::m, "thread-" + i));}threads.forEach((o) -> o.start());threads.forEach((o) -> {try {o.join();} catch (InterruptedException e) {e.printStackTrace();}});System.out.println(t.count);}}好 , 我们来分许分析 , 它的内部实现的原理 , 主要是聊原理 , 它的用法看看API都会用 。 这个原理叫CAS操作 , incrementAndGet() 调用了getAndAddInt
public final int incrementAndGet() { return U.getAndAddInt(this,VALUE,1)+1;}当然这个也是一个CompareAndSetInt操作@HotSportIntrinsicCandidatepublic final int getAndAddInt(Object o,long offset, int delta){int v;do{v=getIntVolatile(o,offset);}while(!weakCompareAndSetInt(o,offset,v,v + delta));return v;}它的内部调调调 , 就会跑到Unsafe类去(不安全的) 。 也就是说AtomicInteger它的内部是调用了Unsafe这个类里面的方法CompareAndSetI(CAS),说一下字面意思 , 比较并且设定 。 这个比较并且设定的意思是什么呢 , 我原来想改变某一个值0, 我想把它变成1 , 但是其中我想做到线程安全 , 就只能加锁synchronized, 不然线程就不安全 。 我现在可以用另外一种操作来替代这把锁 , 就是cas操作 , 你可以把它想象成一个方法 , 这个方法有三个参数 , cas(V , Expected , NewValue) 。
暮年|20年架构师深入讲解java多线程与高并发:volatile与CAS,涨薪5KV第一个参数是要改的那个值;Expected第二个参数是期望当前的这个值会是几;NewValue要设定的新值 。 当前这个线程想改这个值的时候我期望你这值就是0 , 你不能是个1 , 如果是1就说明我这值不对 , 然后想把你变成1 。 这句话说的是什么意思呢 , 比如原来这个值变成3了 , 我这个线程想改这个值的时候我一定期望你现在是3, 是3我才改 , 如果你在我改的过程中变成4了 , 那你跟我的期望值就对不上了 , 说明有另外一个线程改了这个值了 , 那我这个cas就重新再试一下 , 再试的时候我希望你这个值是4 , 在修改的时候期望值是4 , 没有其他的线程修改这个值 , 那好 , 我给你改成5 , 这就是cas操作 , 在本质上就是这么一个意思 。
Expected如果对的上期望值 , NewValue才会去对其修改 , 进行新的值设定的时候 , 这个过程之中来了一个线程把你的值改变了怎么办 , 我就可以再试一遍 , 或者失败 , 这个是cas操作 。
当你判断的时候 , 发现是我期望的值 , 还没有进行新值设定的时候值发生了改变怎么办 , cas是cpu的原语支持 , 也就是说cas操作是cpu指令级别上的支持 , 中间不能被打断 。
ABA问题
一般的面试会问一下 , 了解这个ABA问题吗?
这个ABA问题是这样的 , 假如说你有一个值 , 我拿到这个值是1 , 想把它变成2 , 我拿到1用cas操作 , 期望值是1 , 准备变成2 , 这个对象Object , 在这个过程中 , 没有一个线程改过我肯定是可以更改的 , 但是如果有一个线程先把这个1变成了2后来又变回1 , 中间值更改过 , 它不会影响我这个cas下面操作 , 这就是ABA问题 。
这种问题怎么解决 。 如果是int类型的 , 最终值是你期望的 , 也没有关系 , 这种没关系可以不去管这个问题 。 如果你确实想管这个问题可以加版本号 , 做任何一个值的修改 , 修改完之后加一 , 后面检查的时候连带版本号一起检查 。
如果是基础类型:无所谓 。 不影响结果值;