详解解决CAS机制中ABA问题的AtomicStampedReference

AtomicStampedReference 是一个带有时间戳的对象引用,能很好的解决 CAS 机制中的 ABA 问题,这篇文章将通过案例对其介绍分析。

** 一、ABA 问题 **

ABA 问题是 CAS 机制中出现的一个问题,他的描述是这样的。我们直接画一张图来演示,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ARIFNCNA-1587017953550)(https://pics0.baidu.com/feed/4bed2e738bd4b31cce48475ac556867a9f2ff865.jpeg?token=aa113af878950928b60943f314e932fb&s=6696EC22BF8D50CA4AFD78CB0000B032)]

什么意思呢?就是说一个线程把数据 A 变为了 B,然后又重新变成了 A。此时另外一个线程读取的时候,发现 A 没有变化,就误以为是原来的那个 A。这就是有名的 ABA 问题。ABA 问题会带来什么后果呢?我们举个例子。

一个小偷,把别人家的钱偷了之后又还了回来,还是原来的钱吗,你老婆出轨之后又回来,还是原来的老婆吗?ABA 问题也一样,如果不好好解决就会带来大量的问题。最常见的就是资金问题,也就是别人如果挪用了你的钱,在你发现之前又还了回来。但是别人却已经触犯了法律。

如何去解决这个 ABA 问题呢,就是使用今天所说的 AtomicStampedReference。

** 二、AtomicStampedReference**

1、问题解决

我们先给出一个 ABA 的例子,对 ABA 问题进行场景重现。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AgNEBu1U-1587017953552)(https://pics2.baidu.com/feed/cc11728b4710b91222c13818817d5d069345225e.jpeg?token=06f6baab55bb7770604c3cd9264b6dca&s=BA81A14CDABE864F56E0BD0C0000E0C3)]

在上面的代码中,我们使用张三线程,对 index10->11->10 的变化,然后李四线程读取 index 观察是否有变化,并设置新值。运行一下看看结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nr0hMDor-1587017953554)(https://pics1.baidu.com/feed/8718367adab44aed59eb3ed2f19c2604a08bfbe1.jpeg?token=85664c45286dd521589c19718f690a74&s=5D86AD1A95E05D01487DF4D900008031)]

这个案例重现了 ABA 的问题场景,下面我们看如何使用 AtomicStampedReference 解决这个问题的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A8xibtcV-1587017953555)(https://pics5.baidu.com/feed/4610b912c8fcc3cef1bf4968d0c5778dd53f20f7.jpeg?token=129a7a3f27b8da4b02ab0b0c4bd44bc0&s=B281B14CD3F0886B46E1B50A0000E083)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CjOvm33F-1587017953556)(https://pics5.baidu.com/feed/3801213fb80e7bec8279f0626dae183d9a506b1c.jpeg?token=98563a4e3d6a9244e947aa0b96422f8c&s=3A81A14C13F188694EC5AD0B0000E0C1)]

上面的代码我们再来分析一下,我们会发现 AtomicStampedReference 里面增加了一个时间戳,也就是说每一次修改只需要设置不同的版本好即可。我们先运行一边看看:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QmY8xP5X-1587017953557)(https://pics6.baidu.com/feed/bba1cd11728b4710a7fc0eac814e62f8fd032300.jpeg?token=7a4be328fc39400b17b1953910220f1b&s=A3BA2C2BC1324C200AF438D0000080B4)]

这里使用的是 AtomicStampedReference 的 compareAndSet 函数,这里面有四个参数:

compareAndSet (V expectedReference, V newReference, int expectedStamp, int newStamp)。

(1)第一个参数 expectedReference:表示预期值。

(2)第二个参数 newReference:表示要更新的值。

(3)第三个参数 expectedStamp:表示预期的时间戳。

(4)第四个参数 newStamp:表示要更新的时间戳。

这个 compareAndSet 方法到底是如何实现的,我们深入到源码中看看。

2、源码分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8t5CGhgR-1587017953559)(https://pics6.baidu.com/feed/b21c8701a18b87d6f4f4c1064588893d1e30fdff.jpeg?token=4c3c4cde75c745aaf5cf84344708c59f&s=3A81A14CDBE0B9685CE59C0F000070C1)]

刚刚这四个参数的意思已经说了,我们主要关注的就是实现,首先我们看到的就是这个 Pair,因此想要弄清楚,我们再看看这个 Pair 是什么,

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xJeU7yGp-1587017953560)(https://pics4.baidu.com/feed/48540923dd54564ecfaab273f15e3d87d0584f36.jpeg?token=359bd16e1fd8816ae107a9f614bd0481&s=3A81A14EDBE08D7A1665B08A0000E081)]

在这里我们会发现 Pair 里面只是保存了值 reference 和时间戳 stamp。

在 compareAndSet 方法中最后还调用了 casPair 方法,从名字就可以看到,主要是使用 CAS 机制更新新的值 reference 和时间戳 stamp。我们可以进入这个方法中看看。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vjYibj6D-1587017953560)(https://pics7.baidu.com/feed/a8773912b31bb05182752a8075fa7bb14bede04f.jpeg?token=5cc3fcffc3e513fff6af3796ae4ee417)]

三、总结

其实除了 AtomicStampedReference 类,还有一个原子类也可以解决,就是 AtomicMarkableReference,它不是维护一个版本号,而是维护一个 boolean 类型的标记,用法没有 AtomicStampedReference 灵活。因此也只是在特定的场景下使用。

参考资料

  1. 经典的ABA问题与解决方法
  2. Java并发:CAS、ABA问题、ABA问题解决方案
发布了45 篇原创文章 · 获赞 103 · 访问量 169万+

猜你喜欢

转载自blog.csdn.net/WTUDAN/article/details/105557074
今日推荐