Pages

Saturday, October 29, 2011

Synchronized vs atomic

In a very good book Java Concurrency in Practice one of the first example is about generating sequence of unique numbers. To ways of making it thread-safe are mentioned - using synchronized method and using internally atomic object. Here is another little benchmark, this time - comparing these approaches. The result can be seen from this chart:

And here is the code, that I used to make data for it:


package com.sopovs.moradanen;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class SafeSequenceTest {

    private static final int THREADS_NUM = 40;
    private static final int ITERATIONS = 30;
    private static final int SIZE = 1000000;

    public static void main(String[] args) throws InterruptedException {
        //warming up
        test(new SynchronyzedSequence(), THREADS_NUM);
        test(new AtomicSequence(), THREADS_NUM);

        for (int i = 1; i <= THREADS_NUM; i++) {
            long syncTime = 0, atomicTime = 0;

            for (int j = 0; j < ITERATIONS; j++) {
                syncTime += test(new SynchronyzedSequence(), i);
                atomicTime += test(new AtomicSequence(), i);
            }

            System.out.print(i + " " + TimeUnit.MILLISECONDS.convert(syncTime, TimeUnit.NANOSECONDS));
            System.out.println(" " + TimeUnit.MILLISECONDS.convert(atomicTime, TimeUnit.NANOSECONDS));
        }
    }

    private interface Sequence {
        int getValue();
    }

    private static class SynchronyzedSequence implements Sequence {
        private int value = 0;

        @Override
        public synchronized int getValue() {
            return value++;
        }
    }

    private static class AtomicSequence implements Sequence {
        private AtomicInteger value = new AtomicInteger(0);

        @Override
        public int getValue() {
            return value.incrementAndGet();
        }
    }

    private static final long test(Sequence seq, int numThreads) throws InterruptedException {
        long start = System.nanoTime();
        ExecutorService executor = Executors.newFixedThreadPool(numThreads);

        for (int i = 0; i < numThreads; i++) {
            executor.execute(new SequenceConsumer(seq));
        }
        executor.shutdown();
        executor.awaitTermination(1, TimeUnit.DAYS);
        return System.nanoTime() - start;
    }

    private static class SequenceConsumer implements Runnable {
        private final Sequence sequence;

        public SequenceConsumer(Sequence sequence) {
            this.sequence = sequence;
        }

        @Override
        public void run() {
            for (int i = 0; i < SIZE; i++) {
                //only get and do nothing with it
                sequence.getValue();
            }
        }
    }
}

No comments:

Post a Comment