synchronized & ReentrantLock 的一点疑问
经过JDK1.6对synchronized的进一步优化,通常情况下,synchronized与lock & unlock 效率差别不大,如果大家做一下简单实验应该不容易得出,见http://www.blogjava.net/killme2008/archive/2007/09/14/145195.html中的实验,但在做下面这个实验时,发现两者效率上还是有些区别的。
这是一个最简单的阻塞队列的实现,分别采用synchronized和ReentrantLock来实现,代码如下:
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); } } }}