背景說明: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則顯得使用範圍更廣。