5

I testes how fast the Atomic Integer in multithread with comparing synchronized method, but I got the result that Atomic Integer was slower than synchronized method.

I read Java API Reference and I understood Atomic Integer is faster than synchronized in multithread.

So I want to know the reason why I got the poor result with Atomic Integer. My test code was wrong?

Here is my code

import java.util.concurrent.atomic.AtomicInteger; public class AtomicThreadTest { // Number of thread to run private static final int THREAD_NUM = 1000; // Repetition number to count up private static final int ROOP_NUM = 200000; // Base counter class private abstract class Counter implements Runnable { @Override public void run() { for (int i = 0; i < ROOP_NUM; i++) { increment(); } } // Increment method public abstract void increment(); // Get result of calculation public abstract int getResult(); // Get name of the class @Override public String toString() { return getClass().getSimpleName(); } } // Use no thread safe private static int count = 0; private class NoThreadSafeCounter extends Counter { @Override public void increment() { count++; } @Override public int getResult() { return count; } } // Use volatile private static volatile int volatileCount = 0; private class VolatileCounter extends Counter { @Override public void increment() { volatileCount++; } @Override public int getResult() { return volatileCount; } } // Use synchronized private static int synchronizedCount = 0; private class SynchronizedCounter extends Counter { @Override public synchronized void increment() { synchronizedCount++; } @Override public int getResult() { return synchronizedCount; } } // Use AtomicInteger private static AtomicInteger atomicCount = new AtomicInteger(0); private class AtomicCounter extends Counter { @Override public void increment() { atomicCount.incrementAndGet(); } @Override public int getResult() { return atomicCount.get(); } } public static void main(String[] args) { AtomicThreadTest testClass = new AtomicThreadTest(); Counter[] testCounter = { testClass.new NoThreadSafeCounter(), testClass.new VolatileCounter(), testClass.new SynchronizedCounter(), testClass.new AtomicCounter(), }; for (Counter c : testCounter) { System.out.println("-------------------------"); System.out.printf("Test for class : %s\n", c.toString()); try { test(c); } catch (InterruptedException e) { System.out.println("Test halted"); } finally { System.out.println(""); System.gc(); } } System.out.println("-------------------------"); } public static void test(final Counter counter) throws InterruptedException { System.out.printf("Start with threads : %d, roop : %d\n", THREAD_NUM, ROOP_NUM); final long startTime = System.currentTimeMillis(); // Create THREAD_NUM threads and run them Thread[] threads = new Thread[THREAD_NUM]; for (int i = 0; i < THREAD_NUM; i++) { threads[i] = new Thread(counter); threads[i].start(); } // Wait for all threads other than this end while (Thread.activeCount() > 1) { Thread.sleep(10); } final long endTime = System.currentTimeMillis(); System.out.printf("Result %d, Expected %d\n", counter.getResult(), THREAD_NUM*ROOP_NUM); System.out.printf("Time to calc : %d\n", endTime-startTime); } } 

And I got the result below.

------------------------- Test for class : NoThreadSafeCounter Start with threads : 1000, roop : 200000 Result 198785583, Expected 200000000 Time to calc : 127 ------------------------- Test for class : VolatileCounter Start with threads : 1000, roop : 200000 Result 19162116, Expected 200000000 Time to calc : 4458 ------------------------- Test for class : SynchronizedCounter Start with threads : 1000, roop : 200000 Result 200000000, Expected 200000000 Time to calc : 8426 ------------------------- Test for class : AtomicCounter Start with threads : 1000, roop : 200000 Result 200000000, Expected 200000000 Time to calc : 15190 
10
  • 3
    You should not call System.gc() as you don't know when it will get executed. You are not using a lot of memory, it is unlikely that you ever need to GC during the execution Commented Sep 6, 2015 at 10:26
  • 2
    BTW your synchronization (the while loop at the end) is bad and consumes a lot of CPU. You should use something like a semaphore instead Commented Sep 6, 2015 at 10:28
  • 2
    I have completely different results than you with a JMH benchmark. See it in Github Gist. I have the expected result that AtomicInteger is faster than synchronized. Commented Sep 6, 2015 at 11:30
  • 2
    @user2738844 because you are pinging your CPU every 10 ms to check if the computation is over whereas you could just wait for the completion of all threads with a semaphore without pinging the CPU Commented Sep 6, 2015 at 11:37
  • 3
    In the case of a semaphore, your main thread is going to be blocked until the completion of all tasks. While it is blocked, it will not consume any CPU time whereas your loop is actively waiting, this means that the main thread actively wakes up, sleep, wakes up, sleep etc at a fast frequency. Commented Sep 6, 2015 at 11:59

1 Answer 1

4

Despite of the code issues of your testcase, let's talk about the multithreading problem itself.

If you set THREAD_NUM to a much lower number such as 8 or 4, you will find that your AtomicCounter is a bit faster that SynchronizedCounter. Running with 1000 threads will cost two much CPU on AtomicInteger's CAS (compare-and-swap), which cause it runs slower than synchronized code block.

To prove this, you can implement a LongadderCounter:

private class LongadderCounter extends Counter { @Override public void increment() { longadder.increment(); } @Override public int getResult() { return longadder.intValue(); } } 

You'll find that LongadderCounter much faster. LongAdder to AtomicInteger is ConcurrentHashMap to Collections.synchronizedMap(new HashMap<>()), it divides the counter into multiple parts and do CAS on each part to mitigate race conditions.

Sign up to request clarification or add additional context in comments.

1 Comment

I was surprised at the speed of LongAdder! It took 1/15 of the time with AtomicInteger. And thank you for courteous explanation!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.