读书人

synchronized amp; ReentrantLock 的1点疑

发布时间: 2012-10-21 09:00:07 作者: rapoo

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);            }        }    }}


上面这个实验的结果这里就不贴了,欢迎大家去实验。
做过http://www.blogjava.net/killme2008/archive/2007/09/14/145195.html这个实验的同学应该不容易发现,如果只采用lock&unlock与synchronized差别的确不大。

所以我只能妄断是条件signalAll&await的功劳,或许因为试验方式不对,或者的确是因为这个原因,终究我还没弄透。

signalAll&await的实现原理,这里有些讲解http://www.goldendoc.org/2011/06/juc_condition/

读书人网 >编程

热点推荐