Warm tip: This article is reproduced from serverfault.com, please click

When AtomicInteger is faster than synchronized

发布于 2020-12-06 21:23:30

I've already read a great number of articles where is said that AtomicInteger class works faster than a synchronize construction. I did some tests on AtomicInteger and "synchronized" and in my tests, it occurs that synchronized is much faster than AtomicInteger. I would like to understand what is going wrong: my test class is incorrect or AtomicInteger works faster in other situations?

Here is my test class:

    public class Main {

    public static void main(String[] args) throws InterruptedException
    {
        // creating tester "synchronized" class
        TesterSynchronized testSyn = new TesterSynchronized();

        // Creating 3 threads
        Thread thread1 = new Thread(testSyn);
        Thread thread2 = new Thread(testSyn);
        Thread thread3 = new Thread(testSyn);

        // start time
        long beforeSyn = System.currentTimeMillis();

        // start
        thread1.start();
        thread2.start();
        thread3.start();

        thread1.join();
        thread2.join();
        thread3.join();

        long afterSyn = System.currentTimeMillis();
        long delta = afterSyn - beforeSyn;

        System.out.println("Test synchronized: " + delta + " ms");

        // _______________________________________________________

        // creating tester "atomicInteger" class
        TesterAtomicInteger testAtomic = new TesterAtomicInteger();

        thread1 = new Thread(testAtomic);
        thread2 = new Thread(testAtomic);
        thread3 = new Thread(testAtomic);

        // start time
        long beforeAtomic = System.currentTimeMillis();

        // start
        thread1.start();
        thread2.start();
        thread3.start();

        thread1.join();
        thread2.join();
        thread3.join();

        long afterAtomic = System.currentTimeMillis();
        long deltaAtomic = afterAtomic - beforeAtomic;

        System.out.println("Test atomic integer: " + deltaAtomic + " ms");
    }
}

// Synchronized tester
class TesterSynchronized implements Runnable {
    public int integerValue = 0;

    public synchronized void run() {
        for (int i = 0; i < 1_000_000; i++)
            integerValue++;
    }
}

// AtomicInteger class tester
class TesterAtomicInteger implements Runnable {
    AtomicInteger atomicInteger = new AtomicInteger(0);

    public void run() {
        for (int i = 0; i < 1_000_000; i++)
            atomicInteger.incrementAndGet();
    }
}

Test parameters: 3 threads and 1_000_000 increments; Result:

Test synchronized: 7 ms. Test atomic integer: 51 ms

I would be glad to understand why this is happening.

UPD The test will be correct if change the synchronized method to a synchronized block.

// Synchronized tester
class TesterSynchronized implements Runnable {
    public int integerValue = 0;

    public void run() {
        for (int i = 0; i < 1_000_000; i++) {
            synchronized (this) {
                integerValue++;
            }
        }
    }
}
Questioner
gurmigou
Viewed
0
Tom Hawtin - tackline 2020-12-07 05:34:53

The obvious difference in your code is that the AtomicIntger version allows interleaving of threads with ever access, whereas the synchronized version does the entire loop each thread in turn.

There may be other issues. For instance, the JVM may merge multiple invocations of a synchronized block. Depending on the platform, incrementAndGet may not be an atomic operation but implemented as a CAS-loop - if contention is high that could be a problem (I am not entirely sure on that).

Whichever way it is arranged, if you have multiple threads concurrently modifying the same memory location it will not be fast.