ReentrantReadWriteLock與AtomicInteger的簡單應用與對比

背景說明:java

  模擬春節搶票場景,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)模擬複雜業務,兩者執行時間分別爲:this

    ReentrantReadWriteLock實現版 14904 millis, AtomicInteger實現版 402 millis.spa

當把sleep(30 millis)移動到takeTicket()以外後,ReentrantReadWriteLock實現版 執行時間爲:410 millis.線程

當取消sleep(30 millis)模擬複雜業務時,兩者執行時間分別爲:code

    ReentrantReadWriteLock實現版 116 millis, AtomicInteger實現版 103 millis.進程


  以上比較能夠看出,當ReentrantReadWriteLock爲較爲複雜的業務加鎖時,執行時間將較大差距於AtomicInteger.decrementAndGet();而當ReentrantReadWriteLock爲簡單業務加鎖時,執行時間僅僅略慢於AtomicInteger.decrementAndGet(),且幾乎忽略不計。可見,ReentrantReadWriteLock的執行時間將取決於加鎖的業務範圍,而AtomicInteger則只關注關鍵操做,使用ReentrantReadWriteLock時應儘可能縮小加鎖的範圍,不然效率上將不如於AtomicInteger。rem

  同時,在使用中也出現AtomicInteger的一點小問題,就是當某線程進入AtomicInteger.decrementAndGet() < 0的狀況時,其餘線程繼續AtomicInteger.get()請求的話,會出現餘量爲-1的狀況,ReentrantReadWriteLock則不會,在實際使用中應注意這種狀況。

  相對於實際應用場景來講,AtomicInteger在使用變量進行計算時,能夠較好地發揮做用;而在操做數據庫等複雜的應用場景中,ReentrantReadWriteLock則顯得使用範圍更廣。

相關文章
相關標籤/搜索