内容简介:AtomicInteger 与 ABA 问题
今天,来分享下 AtomicInteger 与 ABA 问题。
java.util.concurrent.atomic 包下 AtomicBoolean、 AtomicInteger 、AtomicLong 等以 Atomic* 开头的类原理是一致的,都采用基于 CAS 的乐观锁实现。
CAS 对于一个要更新的变量 V,我们提供一个它的旧值 A 和新值 B,如果变量 V 的值等于旧值 A,那么更新成功,否则更新失败,这个过程是原子性的。其中,这个过程存在 ABA 问题,即如果另一个线程修改 V 值假设原来是 A,先修改成 B,再修改回成 A。当前线程的 CAS 操作无法分辨当前 V 值是否发生过变化。举个例子,线程 1 查询 V 的值为 A 与旧值 A 比较,值相等。线程 2 查询 V 的值为 A 与旧值 A 比较,值相等,更新值为 B。线程 1 更新值为 A。这样, V 的值又被更新成 A 了。
public class AtomicIntegerDemo { private static AtomicInteger atomicInteger = new AtomicInteger(100); public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(new Runnable() { @Override public void run() { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } atomicInteger.compareAndSet(100, 110); atomicInteger.compareAndSet(110, 100); } }); Thread thread2 = new Thread(new Runnable() { @Override public void run() { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("AtomicInteger compareAndSet : " + atomicInteger.compareAndSet(100, 120)); System.out.println("AtomicInteger value : " + atomicInteger.get()); } }); thread1.start(); thread2.start(); thread1.join(); thread2.join(); } }
ABA 问题的解决思路就是使用版本号。从 Java 1.5 开始,JDK 的 atomic 包里提供了一个类 AtomicStampedReference 来解决 ABA 问题。
public class AtomicStampedReferenceDemo { private static AtomicStampedReference atomicStampedReference = new AtomicStampedReference(100, 1); public static void main(String[] args) throws InterruptedException { Thread thread1 = new Thread(new Runnable() { @Override public void run() { int stamp = atomicStampedReference.getStamp(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } atomicStampedReference.compareAndSet(100, 110, stamp, stamp + 1); atomicStampedReference.compareAndSet(110, 100, stamp, stamp + 1); } }); Thread thread2 = new Thread(new Runnable() { @Override public void run() { int stamp = atomicStampedReference.getStamp(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("AtomicStampedReference compareAndSet : " + atomicStampedReference.compareAndSet(100, 120, stamp, stamp + 1)); } }); thread1.start(); thread2.start(); thread1.join(); thread2.join(); } }
这里,阅读下 AtomicStampedReference 类的源码。其中,compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) 方法有四个参数,分别表示:预期引用,更新后的引用,预期标志,更新后的标志。它的作用是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值。
public boolean compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) { Pair<V> current = pair; return expectedReference == current.reference && expectedStamp == current.stamp && ((newReference == current.reference && newStamp == current.stamp) || casPair(current, Pair.of(newReference, newStamp))); }
(完)
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- acme.sh 续期问题(路径问题)
- 缓存的一些问题和一些加密算法【缓存问题】
- 如何把设计问题转化为数学问题(方法论)
- 推荐系统中的冷启动问题和探索利用问题
- GraphQL 教程(六)—— N+1问题和缓存等问题
- Golang 并发问题(四)之单核上的并发问题
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Effective JavaScript
David Herman / Addison-Wesley Professional / 2012-12-6 / USD 39.99
"It's uncommon to have a programming language wonk who can speak in such comfortable and friendly language as David does. His walk through the syntax and semantics of JavaScript is both charming and h......一起来看看 《Effective JavaScript》 这本书的介绍吧!