我正在学习java.util.concurrent库,并在源代码中找到了许多无限循环,就像这个
//java.util.concurrent.atomic.AtomicInteger by Doug Lea
public final int getAndSet(int newValue) {
for (;;) {
int current = get();
if (compareAndSet(current, newValue))
return current;
}
}我想知道,在什么情况下,实际值不能等于预期值(在本例中,compareAndSet返回false)?
发布于 2011-02-06 12:37:49
当在另一个线程中修改该值时,get()和compareAndSet()可以看到不同的值。这是并发库需要担心的事情。
发布于 2011-02-06 12:48:30
许多现代CPU都有到原子硬件操作的compareAndSet()映射。这意味着,它是不需要同步的线程安全(相比之下,这是一个相对昂贵的操作)。但是,只有compareAndSet()本身是原子的,因此为了使getAndSet() (即将变量设置为给定的值并返回它当时的值,而不可能在两者之间将变量设置为不同的值),代码使用了一个技巧:首先获取值,然后用它刚得到的值和新的值尝试compareAndSet()。如果失败,变量将被另一个线程操作,然后代码再次尝试。
如果compareAndSet()很少失败,即如果没有太多线程同时写入变量,这比使用同步更快。在任何时候都有许多线程写入变量的极端情况下,同步实际上会更快,因为当存在同步开销时,试图访问变量的其他线程将等待并在轮到它们时被唤醒,而不必重复重试操作。
发布于 2011-02-06 22:46:56
这不是一个无限循环,当处理TAS (test and set)算法时,这是很好的实践。循环所做的是(a)从内存中读取(应该是易失性语义) (b)计算一个新值(c),如果旧值同时没有改变,则写入新值。
在数据库领域,这就是所谓的乐观锁定。它利用了共享内存的大多数并发更新都是无竞争的事实,在这种情况下,这是实现共享内存的最便宜的方法。
事实上,这基本上是一个不偏不倚的锁将做什么,在没有竞争的情况下。它将读取锁的值,如果它被解锁,它将执行线程ID的CAS,如果成功,锁现在被持有。如果它失败了,是别人先弄到锁的。然而,锁以一种复杂得多的方式处理失败案例,而不仅仅是一次又一次地重新尝试op。他们会继续阅读一段时间,以防锁被迅速解锁(自旋锁定),然后通常会去睡觉,让其他线程进入,直到他们的回合(指数后退)。
https://stackoverflow.com/questions/4913292
复制相似问题