突击并发编程JUC系列演示代码地址:
https://github.com/mtcarpenter/JavaTutorial
他来了,他来了,他带着 ABA
问题走来了,小伙伴们,大家好,咱们又见面了,突击并发编程 JUC
系列实战原子更新数组类马上就要发车了。
字段类型
如果须要原子地更新某个类里的某个字段时,就须要应用原子更新字段类,Atomic包提供了以下3个类进行原子字段更新。
小试牛刀
AtomicIntegerFieldUpdater案例演示
原子地更新整形字段类,还是那个糊涂少年,在批改本人的票数。
老师类如下:
public class Teacher { /** * 老师名称 */ public volatile String name; /** * 学生投票数 */ public volatile int ticketNum; public Teacher(String name, int ticketNum) { this.name = name; this.ticketNum = ticketNum; } public String getName() { return name; } public int getTicketNum() { return ticketNum; }}
public class AtomicExample8 { private static AtomicIntegerFieldUpdater<Teacher> integerFieldUpdater = AtomicIntegerFieldUpdater.newUpdater(Teacher.class, "ticketNum"); public static void main(String[] args) { // 设置糊涂少年 180票 Teacher teacher = new Teacher("糊涂少年", 180); // 180 System.out.println("getAndIncrement = " + integerFieldUpdater.getAndIncrement(teacher)); System.out.println("get=" + integerFieldUpdater.get(teacher)); }}//getAndIncrement = 180//get=181
要想原子地更新字段类须要两步。
- 第一步,因为原子更新字段类都是抽象类,每次应用的时候必须应用静态方法newUpdater()创立一个更新器,并且须要设置想要更新的类和属性。
- 第二步,更新类的字段(属性)必须应用public volatile修饰符。
AtomicIntegerFieldUpdater
和 AtomicIntegerFieldUpdater
办法根本相似,办法getAndIncrement()
、getAndDecrement()
、getAndAdd()
等办法,跟后面的 Atomiclong
实现也根本类同。
AtomicStampedReference 案例演示
CAS 后面咱们都晓得,比拟替换,要更新的变量和预期值相等,则批改为预期预期值,否则批改失败。所以 CAS 就产生了经典的 ABA 问题,什么是ABA 问题呢?比方一个值原来是A,变成了B,起初又变成了A,那么CAS查看时会发现它的值没有发生变化,然而实际上却是产生了变动的。
加深印象
大学选课小伙伴应该深有体会,一到选课零碎卡,解体重启还在卡,多人选课多人卡,除了领取都会卡。当初有一门网红网课,课程人数限度为 100
人,这门课程相当火爆,选课零碎开启后,半分钟这门课程就达到了 99
人,还残余1
个名额。学生 1
和 学生 2
同时进入零碎,并点击了该课程抉择课程
按钮,每一个名额都有一个相应的版本号进行管制,学生 2 在弹框呈现的时候校园网忽然断了,学生 1 就轻松选到了课程,然而学生1
发现心仪的女生没有选到此课程,就退掉了此课程,学生 2
网络好了 ,界面也呈现尽管还剩下一个名额,然而已被学生 1
操作过,学生 2
拿出上次的版本号,曾经无奈抉择课程。
public class AtomicExample9 { private static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(99, 1); public static void main(String[] args) { Thread student1 = new Thread(() -> { int stamp = atomicStampedReference.getStamp(); System.out.println(Thread.currentThread().getName() + "---首次 stamp: " + stamp); atomicStampedReference.compareAndSet(99, 100, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1); System.out.println(Thread.currentThread().getName() + "---第二次 stamp: " + atomicStampedReference.getStamp()); atomicStampedReference.compareAndSet(100, 99, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1); System.out.println(Thread.currentThread().getName() + "---第三次 stamp: " + atomicStampedReference.getStamp()); }, "student1"); Thread student2 = new Thread(() -> { int stamp = atomicStampedReference.getStamp(); System.out.println(Thread.currentThread().getName() + "---第一次 stamp: " + stamp); try { System.out.println(Thread.currentThread().getName() + "---你的校园网正在尝试从新连贯......"); TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } boolean result = atomicStampedReference.compareAndSet(99, 100, stamp, stamp + 1); System.out.println(Thread.currentThread().getName() + "---批改胜利与否:" + result + " 以后 stamp:" + atomicStampedReference.getStamp()); System.out.println(Thread.currentThread().getName() + "---以后课程已选人数:" + atomicStampedReference.getReference()); }, "student2"); student1.start(); student2.start(); }}
student1---首次 stamp: 1student1---第二次 stamp: 2student1---第三次 stamp: 3student2---第一次 stamp: 3student2---你的校园网正在尝试从新连贯......student2---批改胜利与否:true 以后 stamp:4student2---以后课程已选人数:100
总结
本章简略了介绍了 AtomicIntegerFieldUpdater
原子的更新某一个字段,AtomicStampedReference
能够用于解决JUC
中的 ABA
问题,比照前一个章节 AtomicStampedReference
只有 true
和false
两种抉择。在并发中如果两个版本号一直地切换,依然不能很好地解决 ABA
问题,只是从某种程度升高了ABA
事件产生。而应用 AtomicStampedReference
就能够防止这样的问题。
欢送关注公众号 山间木匠 , 我是小春哥,从事 Java 后端开发,会一点前端、通过继续输入系列技术文章与文会友,如果本文能为您提供帮忙,欢送大家关注、 点赞、分享反对,_咱们下期再见!