突击并发编程 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:1
student1--- 第二次 stamp:2
student1--- 第三次 stamp:3
student2--- 第一次 stamp:3
student2--- 你的校园网正在尝试从新连贯......
student2--- 批改胜利与否:true 以后 stamp:4
student2--- 以后课程已选人数:100
总结
本章简略了介绍了 AtomicIntegerFieldUpdater
原子的更新某一个字段,AtomicStampedReference
能够用于解决 JUC
中的 ABA
问题,比照前一个章节 AtomicStampedReference
只有 true
和 false
两种抉择。在并发中如果两个版本号一直地切换,依然不能很好地解决 ABA
问题,只是从某种程度升高了 ABA
事件产生。而应用 AtomicStampedReference
就能够防止这样的问题。
欢送关注公众号 山间木匠, 我是小春哥,从事 Java 后端开发,会一点前端、通过继续输入系列技术文章与文会友,如果本文能为您提供帮忙,欢送大家关注、点赞、分享反对,_咱们下期再见!