ReentrantReadWriteLockとAtomicIntegerの簡単な応用と比較

4401 ワード

背景の説明:
春節の切符を奪うシーンをシミュレートし、500スレッド(50同時)で100枚の切符を奪い、残りの切符が十分であれば切符を取得することに成功し、そうでなければ成功しない.
ReentrantReadWriteLock実装版:
public class ReadWriteLockTest {

	private static final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
	private static final WriteLock writeLock = readWriteLock.writeLock();
	private static final ReadLock readLock = readWriteLock.readLock();
	private static final ExecutorService pool = Executors.newFixedThreadPool(50);

	private int surplusTickets = 100;//  
	private int surplusThread = 500;//  , 

	/**
	 *  , , 
	 */
	@Test
	public void testReadLock() {
		Date beginTime = new Date();
		for (int i = 0; i < surplusThread; i++) {
			final int runNum = i;
			pool.execute(new Runnable() {
				public void run() {
					boolean getted = takeTicket();

					String gettedMsg = "";
					if (getted) {
						gettedMsg = "has getted";
					} else {
						gettedMsg = "not getted";
					}
					System.out.println("thread " + runNum + " " + gettedMsg + ", remain: " + surplusTickets
							+ ", line up:" + surplusThread + "..");
				}
			});
		}

		while (surplusThread >= 30) {
			sleep(100);
		}
		Date overTime = new Date();
		System.out.println("take times:" + (overTime.getTime() - beginTime.getTime()) + " millis.");
	}

	/**
	 *  
	 */
	private int nowSurplus() {
		readLock.lock();
		int s = this.surplusTickets;
		sleep(30);//  
		readLock.unlock();
		return s;
	}

	/**
	 *  
	 */
	private boolean takeTicket() {
		writeLock.lock();
		boolean result = false;

		if (nowSurplus() > 0) {
			this.surplusTickets -= 1;
			result = true;
		}

		surplusThread -= 1;
		writeLock.unlock();
		return result;
	}

	/**
	 *  
	 */
	private void sleep(int millis) {
		try {
			Thread.sleep(millis);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

AtomicInteger実装:
public class AtomicIntegerTest {

	private static final ExecutorService pool = Executors.newFixedThreadPool(50);

	private AtomicInteger surplusTickets = new AtomicInteger(100);//  
	private int surplusThread = 500;//  , 

	/**
	 *  , , 
	 */
	@Test
	public void testReadLock() {
		Date beginTime = new Date();
		for (int i = 0; i < surplusThread; i++) {
			final int runNum = i;
			pool.execute(new Runnable() {
				public void run() {
					boolean getted = takeTicket();

					String gettedMsg = "";
					if (getted) {
						gettedMsg = "has getted";
					} else {
						gettedMsg = "not getted";
					}
					System.out.println("thread " + runNum + " " + gettedMsg + ", remain: " + surplusTickets
							+ ", line up:" + surplusThread + "..");
				}
			});
		}

		while (surplusThread >= 30) {
			sleep(100);
		}
		Date overTime = new Date();
		System.out.println("take times:" + (overTime.getTime() - beginTime.getTime()) + " millis.");
	}

	/**
	 *  
	 */
	private boolean takeTicket() {
		boolean result = false;
		sleep(30);
		if (surplusTickets.decrementAndGet() >= 0) {
			result = true;
		} else {
			surplusTickets.set(0);
		}
		surplusThread -= 1;
		return result;
	}

	/**
	 *  
	 */
	private void sleep(int millis) {
		try {
			Thread.sleep(millis);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

このときsleep(30 millis)は複雑なビジネスをシミュレートし、両者の実行時間はそれぞれ:
ReentrantReadWriteLock実装版14904 millis,AtomicInteger実装版402 millis.
sleep(30 millis)をtakeTicket()の外に移動すると、ReentrantReadWriteLock実装版の実行時間は410 millisである.
sleep(30 millis)シミュレーションの複雑なビジネスをキャンセルすると、両方の実行時間はそれぞれ次のとおりです.
ReentrantReadWriteLock実装版116 millis,AtomicInteger実装版103 millis.
以上の比較から分かるように、ReentrantReadWriteLockが複雑な業務ロックである場合、実行時間はAtomicIntegerと大きく異なる.decrementAndGet();一方、ReentrantReadWriteLockが単純な業務ロックである場合、実行時間はAtomicIntegerよりわずかに遅い.decrementAndGet()は、ほとんど無視されています.ReentrantReadWriteLockの実行時間は、ロックされたビジネス範囲に依存し、AtomicIntegerは重要な操作にのみ注目し、ReentrantReadWriteLockを使用する場合は、ロックされた範囲をできるだけ縮小する必要があります.そうしないと、効率はAtomicIntegerに及ばないことがわかります.
また、あるスレッドがAtomicIntegerに入るという小さな問題もある.decrementAndGet()<0の場合、他のスレッドはAtomicIntegerを継続する.get()リクエストの場合、残量が-1の場合がありますが、ReentrantReadWriteLockではありませんので、実際の使用では注意してください.
実際の応用シーンに比べて、AtomicIntegerは変数を用いて計算する際に、よりよく機能することができる.データベースの操作など複雑なアプリケーションシーンでは、ReentrantReadWriteLockがより広く使用されています.