生成随机数的方法:
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,每个线程都只能看到自己私有的伪随机数序列,而不是所有线程共享同一个随机数序列,如果能够避免使用共享状态,那么开销将会更小。我们可以通过提高处理竞争的效率来提高可伸缩性,但只有完全消除竞争,才能实现真正的可伸缩性。