package com.j256;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Quick comparison between synchronized, synchronized lock array, AtomicInteger array,
* and Semaphore array increment loops.
*
* My overall point in this is that 10 MILLION iterations show a difference. The unit cost
* of the synchronized block is negligible.
*
* @author graywatson
*/
public class IncrementLockingComparison {
final int NUM_INTEGERS = 100000;
final int NUM_THREADS = 10;
final int[] ints = new int[NUM_INTEGERS];
final AtomicInteger[] atomicIntegers = new AtomicInteger[ints.length];
final Semaphore[] semaphores = new Semaphore[ints.length];
final Object[] locks = new Object[ints.length];
{
for (int i = 0; i < atomicIntegers.length; i++) {
atomicIntegers[i] = new AtomicInteger(0);
}
for (int i = 0; i < semaphores.length; i++) {
semaphores[i] = new Semaphore(1);
}
for (int i = 0; i < locks.length; i++) {
locks[i] = new Object();
}
}
public static void main(String[] args) throws Exception {
IncrementLockingComparison foo = new IncrementLockingComparison();
foo.doSingleLockIncrement();
foo.doMultipleLockIncrement();
foo.doAtomicIncrement();
foo.doSemaphoreIncrement();
}
private void doSingleLockIncrement() throws Exception {
long before = System.currentTimeMillis();
ExecutorService pool = Executors.newCachedThreadPool();
for (int i = 0; i < NUM_THREADS; i++) {
pool.submit(new SingleLockIncrement());
}
pool.shutdown();
pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
long after = System.currentTimeMillis();
System.out.println("Single lock = " + (after - before));
}
private void doMultipleLockIncrement() throws Exception {
long before = System.currentTimeMillis();
ExecutorService pool = Executors.newCachedThreadPool();
for (int i = 0; i < NUM_THREADS; i++) {
pool.submit(new MultipleLockIncrement());
}
pool.shutdown();
pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
long after = System.currentTimeMillis();
System.out.println("Lock array = " + (after - before));
}
private void doAtomicIncrement() throws Exception {
long before = System.currentTimeMillis();
ExecutorService pool = Executors.newCachedThreadPool();
for (int i = 0; i < NUM_THREADS; i++) {
pool.submit(new AtomicIncrement());
}
pool.shutdown();
pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
long after = System.currentTimeMillis();
System.out.println("AtomicInteger array = " + (after - before));
}
private void doSemaphoreIncrement() throws Exception {
long before = System.currentTimeMillis();
ExecutorService pool = Executors.newCachedThreadPool();
for (int i = 0; i < NUM_THREADS; i++) {
pool.submit(new SemaphoreIncrement());
}
pool.shutdown();
pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
long after = System.currentTimeMillis();
System.out.println("Semaphore array = " + (after - before));
}
private void doLocksIncrement() throws Exception {
long before = System.currentTimeMillis();
ExecutorService pool = Executors.newCachedThreadPool();
for (int i = 0; i < NUM_THREADS; i++) {
pool.submit(new MultipleLockIncrement());
}
pool.shutdown();
pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
long after = System.currentTimeMillis();
System.out.println(after - before);
}
private class SingleLockIncrement implements Runnable {
@Override
public void run() {
Random random = new Random();
for (int i = 0; i < 10000000; i++) {
synchronized (ints) {
ints[random.nextInt(ints.length)]++;
}
}
}
}
private class MultipleLockIncrement implements Runnable {
@Override
public void run() {
Random random = new Random();
assert ints.length == locks.length;
for (int i = 0; i < 10000000; i++) {
int which = random.nextInt(ints.length);
synchronized (locks[which]) {
ints[which]++;
}
}
}
}
private class AtomicIncrement implements Runnable {
@Override
public void run() {
Random random = new Random();
for (int i = 0; i < 10000000; i++) {
atomicIntegers[random.nextInt(atomicIntegers.length)].incrementAndGet();
}
}
}
private class SemaphoreIncrement implements Runnable {
@Override
public void run() {
Random random = new Random();
assert ints.length == semaphores.length;
for (int i = 0; i < 10000000; i++) {
int which = random.nextInt(ints.length);
try {
semaphores[which].acquire();
} catch (InterruptedException e) {
return;
}
try {
ints[which]++;
} finally {
semaphores[which].release();
}
}
}
}
}