This article provides test results for performance comparison between synchronized methods against atomic types.



Sun tells us, that the new (jdk1.5) Atomic data types (AtomicLong etc) are faster than the classic way of programming with synchronized blocks. Is it really true? We made a test:
We tested a counter which is incremented from multiple threads concurrently:
package moskito.test.plain.atomiclong;

public interface Counter {
	void increase();
	long getValue();
}
the test programm:
public class Test {
	
	ConcurrentMap threadMap;
	private Counter counter;
	
	public static final int THREAD_COUNT = 200;
	public static final int OPERATIONS_EACH_THREAD = 10000;
	
	long startTime;
	
	Test(Counter counter){
		this.counter = counter;
	}
	
	public void start(){
		//create threads
		threadMap = new ConcurrentHashMap(THREAD_COUNT);
		for (int i=0; i threads = threadMap.values();
		for (Thread t: threads)
			t.start();
	}
	
	private void end(){
		long endTime = System.currentTimeMillis();
		System.out.println("all threads finished. Counter value is "+counter.getValue());
		long expected = THREAD_COUNT* OPERATIONS_EACH_THREAD;
		if (counter.getValue()==expected)
			System.out.println("Test successful.");
		else
			System.out.println("Test FAILED.");
		System.out.println("Execution lasted: "+(endTime-startTime)+" ms.");
	}
	
	public void notifyRunnerFinished(TestRunner aRunner){
		threadMap.remove(aRunner.getName());
		if (threadMap.isEmpty())
			end();
	}
}

public class TestRunner extends Thread{
	
	private int limit;
	private Counter counter;
	private Test master;
	
	public TestRunner(Test aMaster, Counter aCounter, int aLimit){
		master = aMaster;
		counter = aCounter;
		limit = aLimit;
	}
	
	public void run(){
		for (int i=0; i
and the three tested Counter implementations:
public class AtomicLongCounter implements Counter{
	private AtomicLong value;
	
	public AtomicLongCounter(){
		value = new AtomicLong(0);
	}

	public long getValue() {
		return value.get();
	}

	public void increase() {
		value.incrementAndGet();
	}
}

public class SynchronizedCounter implements Counter{
	private long value;
	private Object lock;
	
	public SynchronizedCounter(){
		value = 0;
		lock = new Object();
	}

	public long getValue() {
		return value;
	}

	public void increase() {
		synchronized(lock){
			value++;
		}
	}
}

public class UnsynchronizedCounter implements Counter{
	private long value;
	
	public UnsynchronizedCounter(){
		value = 0;
	}

	public long getValue() {
		return value;
	}

	public void increase() {
		value++;
	}
	
}

We tested all three counter on three machines, all intel: a Pentium 4 2Ghz, a Pentium 4 HT 3.2 Ghz and a XEON 3.2 Ghz (Please supply your test results too). All test were executed under 32bit VMs under linux debian. The results were quite surprising:



Processor Atomic Synchronized Unsynchronized
Sun JDK 1.5_06 32 bit
P4 370 700 80
P4 HT 210 1250 47
XEON 203 1100 43
P3 1Ghz 260 220 161
jrockit-R26.3.0-jdk1.5.0_06
P4 540 170 120
P4 HT 350 220 66
XEON 300 240 65
The values in cells are average execution times in milliseconds


Conclusion: Hmm? You will probably have to optimize your code for a specific JVM. Sofar for "write once, run everywhere...".

Back to Moskito