尽管不使用锁定,比较和设置如何工作
Posted
技术标签:
【中文标题】尽管不使用锁定,比较和设置如何工作【英文标题】:How does compare-and-set work despite not using locking 【发布时间】:2019-06-03 05:17:48 【问题描述】:如elsewhere 所述,AtomicInteger
、AtomicLong
等类型使用 CAS。 CAS 不使用锁定,它本质上是非常乐观的。它遵循以下步骤:
1) 将原始值与我们手头的值进行比较。 2)如果值不匹配,则意味着中间的某个线程已更改该值。否则它将继续并用新值交换值。
public final long incrementAndGet()
for (;;)
long current = get();
long next = current + 1;
if (compareAndSet(current, next))
return next;
假设两个线程 T1 和 T2 将当前值读取为 1,并且都尝试将值增加到 2。现在,两个线程同时到达行,即 if (compareAndSet(current, next)
) 并尝试更新平行线。如果没有锁定机制,那么两个线程都应该成功并返回 2。但这不会发生。
那么,compareAndSet
在没有获得锁的情况下如何工作?
【问题讨论】:
做最少的研究通常会有所帮助:en.wikipedia.org/wiki/Compare-and-swap。而真正的东西是cpu操作cmpxchg8b / cmpxchg16b
。
【参考方案1】:
CAS 在多处理器 CPU 中使用锁。对于单核处理器,CAS不会使用锁,因为CPU即使在多线程中也会串行执行命令。
JVM 在 cpp 调用中使用 JNI 实现 CAS,cpp CAS 操作将编译为 CPU assemble 命令。所以对于单核多线程的场景,CPU每次都会自动调度和挑选一个线程。所以多线程不会有任何安全问题。
【讨论】:
在单核处理器中,可能会发生上下文切换,并且在获得 CPU 时间并停止第一个线程后,另一个线程可以进入。如果整个操作没有被锁定,它仍然容易出错。虽然我知道,CAS 原子地将 V 更新为新值 B,但前提是 V 中的值与预期的旧值 A 匹配;否则它什么也不做。发生了三个操作。所以问题是即使在单核的情况下也必须有一些锁才能使这些操作原子化 这是由CPU硬件架构实现的,在CPU调度层面,这些操作可以被视为原子的。我不太清楚缓存如何在硬件级别处理这个问题。以上是关于尽管不使用锁定,比较和设置如何工作的主要内容,如果未能解决你的问题,请参考以下文章