简体   繁体   English

java.util.concurrent代码审查

[英]java.util.concurrent code review

I'm study java.util.concurrent library and find many infinite loops in the source code, like this one 我正在研究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;
    }
}

I wonder, in what cases actual value can not be equal to the expected value (in this case compareAndSet returns false)? 我想知道,在什么情况下实际值不能等于期望值(在这种情况下compareAndSet返回false)?

Many modern CPUs have compareAndSet() map to an atomic hardware operation. 许多现代CPU都将compareAndSet()映射到原子硬件操作。 That means, it is threadsafe without requiring synchronization (which is a relatively expensive operation in comparison). 这意味着,它是线程安全的而不需要同步(相比之下,这是一个相对昂贵的操作)。 However, it's only compareAndSet() itself with is atomic, so in order to getAndSet() (ie set the variable to a given value and return the value it had at that time, without the possibility of it being set to a different value in between) the code uses a trick: first it gets the value, then it attempts compareAndSet() with the value it just got and the new value. 但是,它只是compareAndSet()本身是原子的,所以为了getAndSet()getAndSet()变量设置为给定值并返回它当时具有的值,而不可能将其设置为不同的值)代码之间使用一个技巧:首先它获取值,然后它尝试compareAndSet()与刚刚获得的值和新值。 If that fails, the variable was manipulated by another thread inbetween, and the code tries again. 如果失败,则变量由中间的另一个线程操纵,代码再次尝试。

This is faster than using synchronization if compareAndSet() fails rarely, ie if there are not too many threads writing to the variable at the same time. 如果compareAndSet()很少失败,那么这比使用同步更快,即如果没有太多线程同时写入变量。 In an extreme case where many threads are writing to the variable at all times, synchronization can actually be faster because while there is an overhead to synchronize, other threads trying to access the variable will wait and be woken up when it's their turn, rather than having to retry the operation repeatedly. 在许多线程始终写入变量的极端情况下,同步实际上可以更快,因为虽然存在同步开销,但是其他尝试访问变量的线程将等待并在轮到它时被唤醒,而不是必须重复重试该操作。

When the value is modified in another thread, the get() and compareAndSet() can see different values. 当在另一个线程中修改该值时,get()和compareAndSet()可以看到不同的值。 This is the sort of thing a concurrent library needs to worry about. 这是并发库需要担心的问题。

This is not an infinite loop, it is good practice when dealing with a TAS (test and set) algorithm. 不是一个无限循环,在处理TAS(测试和设置)算法时这是一个很好的做法。 What the loop does is (a) read from memory (should be volatile semantics) (b) compute a new value (c) write the new value if the old value has not in the meantime changed . 循环的作用是(a)从内存中读取(应该是易失性语义)(b)计算新值(c) 如果旧值未同时更改则写入新值。

In database land this is known as optimistic locking. 在数据库领域,这称为乐观锁定。 It leverages the fact that most concurrent updates to shared memory are uncontended, and in that case, this is the cheapest possible way to do it. 它利用了共享内存的大多数并发更新是无争用的事实,在这种情况下,这是最便宜的方法。

In fact, this is basically what an unbiased Lock will do in the uncontended case. 事实上,这基本上是无偏见的Lock在无竞争情况下会做的事情。 It will read the value of the lock, and if it is unlocked, it will do a CAS of the thread ID and if that succeeds, the lock is now held. 它将读取锁的值,如果它被解锁,它将执行线程ID的CAS,如果成功,则现在保持锁。 If it fails, someone else got the lock first. 如果失败,其他人首先获得锁定。 Locks though deal with the failure case in a much more sophisticated way than merely retrying the op over and over again. 锁定虽然以一种更复杂的方式处理失败案例,而不仅仅是一遍又一遍地重试op。 They'll keep reading it for a little while incase the lock is quickly unlocked (spin-locking) then usually go to sleep for bit to let other threads in until their turn (exponential back-off). 他们将继续阅读它一段时间,因为锁被快速解锁(旋转锁定)然后通常进入睡眠状态以让其他线程进入直到轮到他们(指数后退)。

Here is an actual usage of the compareAndSet operation: Imagine that you design an algorithm that calculates something in multiple threads. 以下是compareAndSet操作的实际用法:假设您设计了一个计算多个线程中的内容的算法。

Each thread remembers an old value and based on it performs a complicated calculation. 每个线程都会记住一个旧值,并根据它执行复杂的计算。

Then it wants to set the new result ONLY if the old value hasn't been already changed by another calculation thread. 然后,如果旧值尚未被另一个计算线程更改,它只想设置新结果。 If the old value is not the expected one the thread discards its own work, takes a new value and restarts the calculations. 如果旧值不是预期值,则线程会丢弃其自己的工作,获取新值并重新开始计算。 It uses compareAndSet for that. 它使用了compareAndSet

Further other threads are guaranteed to get only fresh values to continue the calculations. 其他线程保证只获得新值以继续计算。

The "infinite" loops are used to implement "busy waiting" which might be much less expensive than putting the thread to sleep especially when the thread contention is low. “无限”循环用于实现“忙等待”,这可能比将线程置于睡眠状态要便宜得多,尤其是在线程争用较低时。

Cheers! 干杯!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM