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(); } } } } }