package book.example.threading.races;
import java.util.concurrent.*;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
/**
* A class that "blitzes" an object by calling it many times, from
* multiple threads. Used for stress-testing synchronisation.
*
* @author nat
*/
public class MultithreadedStressTester {
/**
* The default number of threads to run concurrently.
*/
public static final int DEFAULT_THREAD_COUNT = 2;
private final ExecutorService executor;
private final int threadCount;
private final int iterationCount;
public MultithreadedStressTester(int iterationCount) {
this(DEFAULT_THREAD_COUNT, iterationCount);
}
public MultithreadedStressTester(int threadCount, int iterationCount) {
this.threadCount = threadCount;
this.iterationCount = iterationCount;
this.executor = Executors.newCachedThreadPool();
}
public MultithreadedStressTester(int threadCount, int iterationCount, ThreadFactory threadFactory) {
this.threadCount = threadCount;
this.iterationCount = iterationCount;
this.executor = Executors.newCachedThreadPool(threadFactory);
}
public int totalActionCount() {
return threadCount * iterationCount;
}
public void stress(final Runnable action) throws InterruptedException {
spawnThreads(action).await();
}
public void blitz(long timeoutMs, final Runnable action) throws InterruptedException, TimeoutException {
if (!spawnThreads(action).await(timeoutMs, MILLISECONDS)) {
throw new TimeoutException("timed out waiting for blitzed actions to complete successfully");
}
}
private CountDownLatch spawnThreads(final Runnable action) {
final CountDownLatch finished = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
executor.execute(new Runnable() {
public void run() {
try {
repeat(action);
}
finally {
finished.countDown();
}
}
});
}
return finished;
}
private void repeat(Runnable action) {
for (int i = 0; i < iterationCount; i++) {
action.run();
}
}
public void shutdown() {
executor.shutdown();
}
}