鎖Lock與原子變數Atomic的效能比較
阿新 • • 發佈:2019-01-03
生成隨機數的方法:
public class PseudoRandom {
int calculateNext(int prev) {
prev ^= prev << 6;
prev ^= prev >>> 21;
prev ^= (prev << 7);
return prev;
}
}
在隨機數字生成器中,在生成下一個隨機數字時需要用到上一個數字,所以在隨機數字生成器中必須記錄前一個數值並將其作為狀態的一部分。
基於ReentrantLock實現的隨機數生成器
@ThreadSafe
public class ReentrantLockPseudoRandom extends PseudoRandom {
private final Lock lock = new ReentrantLock(false);
private int seed;
ReentrantLockPseudoRandom(int seed) {
this.seed = seed;
}
public int nextInt(int n) {
lock.lock();
try {
int s = seed;
seed = calculateNext(s);
int remainder = s % n;
return remainder > 0 ? remainder : remainder + n;
} finally {
lock.unlock();
}
}
}
基於AtomicInteger實現的隨機數生成器
@ThreadSafe
public class AtomicPseudoRandom extends PseudoRandom {
private AtomicInteger seed;
AtomicPseudoRandom(int seed) {
this.seed = new AtomicInteger(seed);
}
public int nextInt(int n) {
while (true) {
int s = seed.get();
int nextSeed = calculateNext(s);
if (seed.compareAndSet(s, nextSeed)) {
int remainder = s % n;
return remainder > 0 ? remainder : remainder + n;
}
}
}
}
在中低程度的競爭下,原子變數能提供更高的可伸縮性(鎖在發生競爭時會掛起執行緒,從而降低了CPU的使用率和共享記憶體總線上的同步通訊量),而在高強度的競爭下,鎖能夠更有效地避免競爭。
在單CPU的系統上,基於CAS的演算法在效能上同樣會超過基於鎖的演算法,因為CAS在單CPU的系統上通常能執行成功,只有在偶然情況下,執行緒才會在執行讀-改-寫的操作過程中被其他執行緒搶佔執行。
使用執行緒私有變數ThreadLocal,每個執行緒都只能看到自己私有的偽隨機數序列,而不是所有執行緒共享同一個隨機數序列,如果能夠避免使用共享狀態,那麼開銷將會更小。我們可以通過提高處理競爭的效率來提高可伸縮性,但只有完全消除競爭,才能實現真正的可伸縮性。