synchronized block vs AtomicLong

Cage Match!

Synchronized Block

MyCounter.java
package biz.tugay;
 
public class MyCounter {
 
    private long count;
 
    public void increment() {
        count++;
    }
 
    public long getCount() {
        return count;
    }
}

MyCounterTest.java
package biz.tugay;
 
public class MyCounterTest {
 
    public static void main(String[] args) throws InterruptedException {
 
        final long start = System.currentTimeMillis();
 
        final MyCounter myCounter = new MyCounter();
 
        final Runnable myCounterIncrementer = new Runnable() {
            @Override
            public void run() {
                for (long i = 0; i < 100000000L; i++) {
                    synchronized (myCounter) {
                        myCounter.increment();
                    }
                }
            }
        };
 
        final Thread a = new Thread(myCounterIncrementer);
        final Thread b = new Thread(myCounterIncrementer);
 
        a.start();
        b.start();
 
        a.join();
        b.join();
 
        System.out.println("Count is: " + myCounter.getCount());
 
        final long end = System.currentTimeMillis();
        final long tookMillis = end - start;
        System.out.println("Took: " + tookMillis + " milliseconds..");
    }
}

and the output will be..
Count is: 200000000
Took: 7277 milliseconds..

AtomicLong

MyCounter.java
package biz.tugay;
 
import java.util.concurrent.atomic.AtomicLong;
 
public class MyCounter {
 
    private AtomicLong count = new AtomicLong();
 
    public void increment() {
        count.getAndIncrement();
    }
 
    public long getCount() {
        return count.longValue();
    }
}

MyCounterTest.java
package biz.tugay;
 
public class MyCounterTest {
 
    public static void main(String[] args) throws InterruptedException {
 
        final long start = System.currentTimeMillis();
 
        final MyCounter myCounter = new MyCounter();
 
        final Runnable myCounterIncrementer = new Runnable() {
            @Override
            public void run() {
                for (long i = 0; i < 100000000L; i++) {
                    myCounter.increment();
                }
            }
        };
 
        final Thread a = new Thread(myCounterIncrementer);
        final Thread b = new Thread(myCounterIncrementer);
 
        a.start();
        b.start();
 
        a.join();
        b.join();
 
        System.out.println("Count is: " + myCounter.getCount());
 
        final long end = System.currentTimeMillis();
        final long tookMillis = end - start;
        System.out.println("Took: " + tookMillis + " milliseconds..");
    }
}

and the output will be..
Count is: 200000000
Took: 2169 milliseconds..

So?

AtomicLong version seems to be 3 times faster!