synchronized&Rentrant Lockのちょっとした疑問
8397 ワード
JDK 1.6を経て、synchronizedをさらに最適化しました.通常、synchronizedとロック&unlockの効率はあまり違いません.簡単な実験をすれば、簡単には出られないはずです.
http://www.blogjava.net/killme2008/archive/2007/09/14/145195.htmlにおける実験ですが、この実験を行ったところ、やはり効率には違いがあります.
これは、システムnchronizedとRentrantrant Lockをそれぞれ採用して実装される最も簡単なブロック列である.コードは以下の通りである.
やったことがあります
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/
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/