如何像 CAS 一样“原子地验证和延迟交换”?
Posted
技术标签:
【中文标题】如何像 CAS 一样“原子地验证和延迟交换”?【英文标题】:How to "'validate and lazy swap' atomically" like CAS? 【发布时间】:2017-08-03 13:50:18 【问题描述】:这是我想要的函数原型:
atomicReference.validateAndSwap(value -> isInvalid(value), () -> createValid());
假定它是从多个线程调用的。
仅当第一次返回 true
时才调用第二个 lambda。
第一个(如果第一个返回 true
则加上第二个)lambda 调用应该是单个原子操作。
甚至可以不用synchronized
来实现?
是否有针对类似功能的现成解决方案?
我是不是思维方式不对,错过了什么?
【问题讨论】:
"不同步也可以实现?" - 不。 这里的对象是不可变的吗?如果不是,它们的“有效性”至少是不变的吗?如果两个答案都是“否”,那么我怀疑你忽略了一些关于安全共享可变状态的非常基本的东西。如果任一答案为“是”,这可能是可能的,但它需要您放宽对 lambdas 调用作为原子单元发生的要求。如果有效性确实是不可变的,那应该不是问题,除非您坚持不产生垃圾。 另外,如果您可以提供更多关于您最终想要做什么的背景信息,我们可以提供更多有用的反馈,并可能为这项工作推荐一个更好的“工具”。 【参考方案1】:我不确定您所说的“第一个(如果第一个返回 true,则加上第二个)lambda 调用应该是单个原子操作。”时您的意思是否正确。 原子引用的要点是更新函数评估可能重叠,因此,不应该有干扰,但会表现得好像是原子的,因为当评估重叠时,只有一个可以成功 CAS
而另一个必须重复关于新值。
如果您想要真正的原子评估,使用Lock
或synchronized
是不可避免的。如果您有适当的非干扰功能并希望像原子一样实现更新,则可以像这样实现
Value old;
do old = atomicReference.get();
while(isInvalid(old) && !atomicReference.compareAndSet(old, createValid()));
由于在这种特定情况下,createValid()
函数不依赖于旧值,我们可以避免在竞争情况下重复评估:
Value old = atomicReference.get();
if(isInvalid(old))
Value newValue = createValid();
while(!atomicReference.compareAndSet(old, newValue))
old=atomicReference.get();
if(!isInvalid(old)) break;
所有假设都假设对象的有效性不能在两者之间改变。否则,锁定或同步是不可避免的。
请注意,Java 8 的更新方法遵循相同的原则。所以你可以写
atomicReference.updateAndGet(old -> isInvalid(old)? createValid(): old);
达到同样的效果,但它也不是真正的原子,而是在更新函数的并发评估没有干扰的情况下表现得好像是原子的。
【讨论】:
如果对象不能在两者之间改变,那么while(isInvalid(old) && !atomicReference.compareAndSet(old, createValid()))
的意义何在?
@Eugene:这是一个do … while
循环;这就是我缩进while
行的原因。不幸的是,它不适合一行。
我的意思是不同的,但我想我现在明白了。这实际上非常好,所以如果isInvalid
返回 true,那么您正在尝试使用有效的 CAS old;如果失败,则意味着 atomicReference 已更改(很可能具有有效值)。而您正在再次尝试,但首先检查该值是否有效......
第二种方法更好。如果这个atomicReference.compareAndSet
失败,则意味着atomicReference
已更改,您正在读取设置的新值并检查这个新值是否有效,必要时重复。以上是关于如何像 CAS 一样“原子地验证和延迟交换”?的主要内容,如果未能解决你的问题,请参考以下文章