synchronized&Rentrant Lockのちょっとした疑問


JDK 1.6を経て、synchronizedをさらに最適化しました.通常、synchronizedとロック&unlockの効率はあまり違いません.簡単な実験をすれば、簡単には出られないはずです.
http://www.blogjava.net/killme2008/archive/2007/09/14/145195.htmlにおける実験ですが、この実験を行ったところ、やはり効率には違いがあります.
    これは、システムnchronizedとRentrantrant Lockをそれぞれ採用して実装される最も簡単なブロック列である.コードは以下の通りである.
public interface Queue<E> {
    public void put(E e);
    public E take() throws InterruptedException;
}
import java.util.LinkedList;

public class RawSyncQueue<E> implements Queue<E> {

    private LinkedList<E> queue = new LinkedList<E>();

    public synchronized void put(E e) {

        if (queue.size() == 0) {
            notifyAll();
        }
        queue.add(e);
    }

    public synchronized E take() throws InterruptedException {

        for (;;) {
            if (queue.size() == 0) {
                wait();
            }
            if (queue.size() != 0) {
                return queue.remove(0);
            }
        }
    }

}

import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockSyncQueue<E> implements Queue<E> {

    private Lock          lock     = new ReentrantLock();
    private Condition     notEmpty = lock.newCondition();
    private LinkedList<E> queue    = new LinkedList<E>();

    public void put(E e) {
        lock.lock();
        if (queue.size() == 0) {
            notEmpty.signalAll();
        }
        queue.add(e);
        lock.unlock();
    }

    public E take() throws InterruptedException {

        lock.lock();
        for (;;) {
            if (queue.size() == 0) {
                notEmpty.await();
            }
            if (queue.size() != 0) {
                E result = queue.remove(0);
                lock.unlock();
                return result;
            }
        }
    }

}
以下の方法でテストを行います.

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

public class PuterTakerTest {

    private static final ExecutorService pool    = Executors.newCachedThreadPool();
    private final AtomicInteger          putSum  = new AtomicInteger(0);
    private final AtomicInteger          takeSum = new AtomicInteger(0);
    private final CyclicBarrier          barrier;
    private final Queue<Integer>         bq;
    private final int                    nTrials, nPairs;

    public static void testAll(int nParis, int tpt) throws Exception {
        System.out.println(nParis + ", " + tpt);
        System.out.print("RawSyncQueue: ");
        Queue<Integer> rawSyncQueue = new RawSyncQueue<Integer>();
        new PuterTakerTest(rawSyncQueue, nParis, tpt).test();
        System.out.print("LockSyncQueue: ");
        Queue<Integer> lockSyncQueue = new LockSyncQueue<Integer>();
        new PuterTakerTest(lockSyncQueue, nParis, tpt).test();
    }

    public static void testAll(int... params) throws Exception {
        if (params.length != 2) {
            throw new IllegalArgumentException();
        }
        testAll(params[0], params[1]);
    }

    public static void main(String[] args) throws Exception {

        int[] params = new int[0];

        params = new int[] { 1, 100000 };
        testAll(params);
        params = new int[] { 2, 100000 };
        testAll(params);
        params = new int[] { 4, 100000 };
        testAll(params);
        params = new int[] { 8, 100000 };
        testAll(params);
        params = new int[] { 16, 100000 };
        testAll(params);
        params = new int[] { 32, 100000 };
        testAll(params);

        params = new int[] { 1, 1000000 };
        testAll(params);
        params = new int[] { 2, 1000000 };
        testAll(params);
        params = new int[] { 4, 1000000 };
        testAll(params);
        params = new int[] { 8, 1000000 };
        testAll(params);
        params = new int[] { 16, 1000000 };
        testAll(params);
        params = new int[] { 32, 1000000 };
        testAll(params);

        pool.shutdown();
    }

    PuterTakerTest(Queue<Integer> queue, int nPairs, int nTrials) throws Exception{
        this.bq = queue;
        this.nTrials = nTrials;
        this.nPairs = nPairs;
        this.barrier = new CyclicBarrier(nPairs * 2 + 1);
    }

    public void test() {
        try {

            for (int i = 0; i < nPairs; ++i) {
                pool.execute(new Producer());
                pool.execute(new Consumer());
            }
            long startTime = System.nanoTime();
            barrier.await();
            barrier.await();
            long nsPerItem = (System.nanoTime() - startTime) / (nPairs * (long) nTrials);
            System.out.println("Throughput: " + nsPerItem + " ns/item");
            assertEquals(putSum.get(), takeSum.get());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static void assertEquals(int a, int b) {
        if (a != b) {
            throw new RuntimeException(a + " is not equals " + b);
        }
    }

    private static int xorShift(int seed) {
        seed ^= seed << 6;
        seed ^= seed >>> 21;
        seed ^= (seed << 7);
        return seed;
    }

    class Producer implements Runnable {

        @Override
        public void run() {
            try {
                int seed = (this.hashCode() ^ (int) System.nanoTime());
                int sum = 0;
                barrier.await();
                for (int i = nTrials; i > 0; --i) {
                    bq.put(seed);
                    sum += seed;
                    seed = xorShift(seed);
                }
                putSum.getAndAdd(sum);
                barrier.await();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    class Consumer implements Runnable {

        @Override
        public void run() {
            try {
                barrier.await();
                int sum = 0;
                for (int i = nTrials; i > 0; --i) {
                    sum += bq.take();
                }
                takeSum.getAndAdd(sum);
                barrier.await();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

}
上の実験の結果はここでは貼りません.実験に行ってください.
やったことがあります
http://www.blogjava.net/killme2008/archive/2007/09/14/145195.htmlこの実験の学友は容易に発見するべきでなくて、もしlock&unlockとsynchronizedだけを採用するならば違いは大きくありません.
だから、条件signal All&awaitの功労と判断するしかないです.試験方法が間違っているせいか、それとも確かにこの原因のためか、結局はまだ分かりません.
signal All&awaitの実現原理について、ここで説明します.
http://www.goldendoc.org/2011/06/juc_condition/