Site Search:

ReentrantLockPseudoRandom.java

 ReentrantLockPseudoRandom.java

import java.util.concurrent.locks.*;

/**
* ReentrantLockPseudoRandom
* <p/>
* Random number generator using ReentrantLock
*
*/
//ThreadSafe
public class ReentrantLockPseudoRandom {
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();
}
}

private int calculateNext(int n) {
n ^= (n << 6);
n ^= (n >>> 21);
n ^= (n << 7);
return n;
}
public static void main(String... args) {
ReentrantLockPseudoRandom rlr = new ReentrantLockPseudoRandom(50); //change this value, rerun, until you see some . print
int totalThreads = 100000;
for(int i = 0; i < totalThreads; i++) {
// new Thread(() -> System.out.print(rlr.nextInt(5))).start();
new Thread(() -> rlr.nextInt(5)).start();
}
System.out.println(rlr.nextInt(5));
}
}

The ReentrantLockPseudoRandom class is a thread-safe pseudo-random number generator that uses an explicit ReentrantLock to guard access to a shared mutable seed. The nextInt(int n) method locks access to ensure exclusive modification of the seed variable, computes the next seed using a bitwise transformation (calculateNext()), and returns a bounded random value. The use of lock.lock() and lock.unlock() ensures mutual exclusion, preventing race conditions among concurrent threads.

Compared to the previous class, AtomicPseudoRandom, which uses lock-free synchronization via an AtomicInteger and a CAS loop, this version uses blocking synchronization. While both are functionally correct and thread-safe, they differ in performance characteristics:

FeatureAtomicPseudoRandomReentrantLockPseudoRandom
Synchronization typeLock-free, non-blocking (CAS)Blocking with ReentrantLock
Concurrency performanceScales better under high contentionMay degrade under high contention
Fairness/orderingNo fairness guaranteesConfigurable fairness (true/false)
Starvation riskLow (eventually succeeds via retries)Moderate (depending on scheduling)
ComplexityHigher (requires retry logic)Lower (sequential logic within lock)
CPU usage under contentionHigher (spins on retry)Lower (waits on lock)

In short, AtomicPseudoRandom is more efficient and scalable under high concurrency but more complex and potentially CPU-intensive under contention, while ReentrantLockPseudoRandom is simpler and may be more predictable under moderate load but suffers performance penalties under heavy contention. These two implementations illustrate the trade-offs between nonblocking and blocking synchronization techniques in concurrent programming.

No comments:

Post a Comment