一、目錄
二、AQS簡要分析
- 為什么會產生ArrayList、LinkedList、HashMap這些容器?它們底層實現無非都是對數組、鏈表、樹的操作,至於它們的產生,就是因為對編程人員對於數組、鏈表、樹的增刪改查操作非常繁瑣而提出的解決方案。
- 那為什么會產生AQS呢?談到同步,大家最容易想到的就是在多線程中如何確保安全的資源共享。那同步隊列就是為了解決資源共享的同步容器。像上述容器一樣,在頂層就設計好,編程人員只需要調用接口就能輕易實現復雜的資源共享問題。
- 調用自定義同步器的tryAcquire()嘗試直接去獲取資源,如果成功就返回。
- 沒成功,則addWaiter()將線程加入等待隊列的尾部,並標記為獨享模式。
- acquireQueued()使線程在等待隊列中休息,有機會時會去嘗試獲得資源。獲得資源后返回。如果整個過程有中斷過返回true,否則返回false。
- 如果線程在等待過程中中斷過,它是不響應的。只是獲得資源后才再進行自我中斷selfInterrupt(),將中斷補上。


- tryAcquireShared()嘗試獲取資源,成功則直接返回。
- 失敗則通過 doAcquireShared()進入等待隊列,直到被喚醒或者中斷並且成功獲取資源才返回。
- 不同:獨占式是只喚醒后繼節點。共享式是喚醒后繼,后繼還會去喚醒它的后繼,從而實現共享。
以上是核心的關於CountDownLatch、ReentrantLock的分析。由於博主研究程度有限,想更深層次研究,請參考:Java並發AQS詳解
三、淺談CountDownLatch
/** * CountDownLatch相當於指令槍或者門閂,所有線程都awit()阻塞在起跑線,只有countDown到state為0,其他線程才能往下運行。 * @author qiuyongAaron */ public class CountDownLatchDemo { private static final int PLAYER_NUM=5;</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) { CountDownLatch start</span>=<span style="color: #0000ff;">new</span> CountDownLatch(1<span style="color: #000000;">); CountDownLatch end </span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> CountDownLatch(PLAYER_NUM); Player [] players</span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> Player[PLAYER_NUM]; </span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> i=0;i<PLAYER_NUM;i++<span style="color: #000000;">) players[i]</span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> Player(start, end, i); </span><span style="color: #008000;">//</span><span style="color: #008000;">指定線程個數的線程池!</span> ExecutorService exe=<span style="color: #000000;">Executors.newFixedThreadPool(PLAYER_NUM); </span><span style="color: #0000ff;">for</span><span style="color: #000000;">(Player player:players) exe.execute(player); System.out.println(</span>"比賽開始!"<span style="color: #000000;">); </span><span style="color: #008000;">//</span><span style="color: #008000;">比賽開始!</span>
start.countDown();
</span><span style="color: #0000ff;">try</span><span style="color: #000000;"> { end.await(); } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) { e.printStackTrace(); }</span><span style="color: #0000ff;">finally</span><span style="color: #000000;">{ System.out.println(</span>"比賽結束!"<span style="color: #000000;">); exe.shutdown(); } }
}
class Player implements Runnable{
private CountDownLatch start;
private CountDownLatch end;
private int id;Random random</span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> Random(); </span><span style="color: #0000ff;">public</span> Player(CountDownLatch start,CountDownLatch end,<span style="color: #0000ff;">int</span><span style="color: #000000;"> id) { </span><span style="color: #0000ff;">this</span>.start=<span style="color: #000000;">start; </span><span style="color: #0000ff;">this</span>.end=<span style="color: #000000;">end; </span><span style="color: #0000ff;">this</span>.id=<span style="color: #000000;">id; } @Override </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> run() { </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> { </span><span style="color: #008000;">//</span><span style="color: #008000;">等待比賽開始。</span>
start.await();
TimeUnit.SECONDS.sleep(random.nextInt(10));
System.out.println("Player-"+id+":arrived");
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
//選手-id到達終點,end計數為0結束比賽!
end.countDown();
}
}
}//運行結果:
比賽開始!
Player-3:arrived
Player-4:arrived
Player-0:arrived
Player-1:arrived
Player-2:arrived
比賽結束!
三、談ReentrantLock

/** * 示例一:同步鎖的使用 * reentrantlock用於替代synchronized * 本例中由於m1鎖定this,只有m1執行完畢的時候,m2才能執行 * @author qiuyongAaron */ public class ReentrantLockOne { public synchronized void m1(){ for(int i=0;i<10;i++){ try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(i); } }</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">synchronized</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> m2(){ System.out.println(</span>"hello m2!"<span style="color: #000000;">); } </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) { ReentrantLockOne lock</span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> ReentrantLockOne(); </span><span style="color: #0000ff;">new</span> Thread(()->lock.m1(),"t1"<span style="color: #000000;">).start(); </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> { TimeUnit.SECONDS.sleep(</span>2<span style="color: #000000;">); } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) { e.printStackTrace(); } </span><span style="color: #0000ff;">new</span> Thread(()->lock.m2(),"t2"<span style="color: #000000;">).start(); }
}
2.2 ReentrantLock實現線程同步-與synchronized作用一致!

/** * 示例二:等價於同步鎖 * 使用reentrantlock可以完成同樣的功能 * 需要注意的是,必須要必須要必須要手動釋放鎖(重要的事情說三遍) * 使用syn鎖定的話如果遇到異常,jvm會自動釋放鎖,但是lock必須手動釋放鎖,因此經常在finally中進行鎖的釋放 * @author qiuyongAaron */ public class ReentrantLockTwo { ReentrantLock lock =new ReentrantLock(); public void m1(){ try { lock.lock(); for(int i=0;i<10;i++){ TimeUnit.SECONDS.sleep(1); System.out.println(i); } } catch (InterruptedException e) { e.printStackTrace(); }finally{ lock.unlock(); } }</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">synchronized</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> m2(){ lock.lock(); System.out.println(</span>"hello m2!"<span style="color: #000000;">); lock.unlock(); } </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) { ReentrantLockTwo lock</span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> ReentrantLockTwo(); </span><span style="color: #0000ff;">new</span> Thread(()->lock.m1(),"t1"<span style="color: #000000;">).start(); </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> { TimeUnit.SECONDS.sleep(</span>2<span style="color: #000000;">); } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) { e.printStackTrace(); } </span><span style="color: #0000ff;">new</span> Thread(()->lock.m2(),"t2"<span style="color: #000000;">).start(); }
}

/** * 示例三:tryLock * 使用reentrantlock可以進行“嘗試鎖定”tryLock,這樣無法鎖定,或者在指定時間內無法鎖定,線程可以決定是否繼續等待 * @author qiuyongAaron */ public class ReentrantLockThree { ReentrantLock lock=new ReentrantLock();</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> m1(){ </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> { lock.lock(); </span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> i=0;i<10;i++<span style="color: #000000;">){ TimeUnit.SECONDS.sleep(</span>1<span style="color: #000000;">); System.out.println(i); } } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (Exception e) { e.printStackTrace(); }</span><span style="color: #0000ff;">finally</span><span style="color: #000000;">{ lock.unlock(); } } </span><span style="color: #0000ff;">boolean</span> locked=<span style="color: #0000ff;">false</span><span style="color: #000000;">; </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> m2(){ </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> { lock.tryLock(</span>5<span style="color: #000000;">,TimeUnit.SECONDS); System.out.println(</span>"m2:"+<span style="color: #000000;">locked); } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (Exception e) { e.printStackTrace(); }</span><span style="color: #0000ff;">finally</span><span style="color: #000000;">{ </span><span style="color: #0000ff;">if</span><span style="color: #000000;">(locked) lock.unlock(); } } </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) { ReentrantLockThree lock</span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> ReentrantLockThree(); </span><span style="color: #0000ff;">new</span> Thread(()->lock.m1(),"t1"<span style="color: #000000;">).start(); </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> { TimeUnit.SECONDS.sleep(</span>1<span style="color: #000000;">); } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) { e.printStackTrace(); } </span><span style="color: #0000ff;">new</span> Thread(()->lock.m2(),"t2"<span style="color: #000000;">).start(); }
}
2.4 指定公平鎖或者搶占式鎖

/** * ReentrantLock還可以指定為公平鎖 * @author qiuyongAaron */ public class ReentrantLockFive extends Thread{</span><span style="color: #008000;">//</span><span style="color: #008000;">默認false:為非公平鎖 true:公平鎖</span> ReentrantLock lock=<span style="color: #0000ff;">new</span><span style="color: #000000;"> ReentrantLock(); @Override </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> run() { </span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> i=0;i<100;i++<span style="color: #000000;">){ lock.lock(); </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> { TimeUnit.SECONDS.sleep(</span>1<span style="color: #000000;">); System.out.println(Thread.currentThread().getName()</span>+"獲得鎖"+"-"+<span style="color: #000000;">i); } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) { e.printStackTrace(); }</span><span style="color: #0000ff;">finally</span><span style="color: #000000;">{ lock.unlock(); } } } </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) { ReentrantLockFive lock</span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> ReentrantLockFive(); </span><span style="color: #0000ff;">new</span> Thread(lock,"t1"<span style="color: #000000;">).start(); </span><span style="color: #0000ff;">new</span> Thread(lock,"t2"<span style="color: #000000;">).start(); }
}
運行結果:
//非公平鎖
t2獲得鎖-0 t2獲得鎖-1 t1獲得鎖-0 t1獲得鎖-1 t1獲得鎖-2 t2獲得鎖-2
//公平鎖
t1獲得鎖-0 t2獲得鎖-0 t1獲得鎖-1 t2獲得鎖-1 t1獲得鎖-2 t2獲得鎖-2
/** * 模擬生產者消費者模式-線程之間通信 synchronized-notifyAll/wait * @author qiuyongAaron */ public class MyContainerOne { LinkedList<Integer> list=new LinkedList<Integer>(); static final int MAX=10; int count=0;</span><span style="color: #008000;">//</span><span style="color: #008000;">生產者線程</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">synchronized</span> <span style="color: #0000ff;">void</span> put(<span style="color: #0000ff;">int</span><span style="color: #000000;"> i){ </span><span style="color: #0000ff;">while</span>(list.size()==<span style="color: #000000;">MAX){ </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> { </span><span style="color: #0000ff;">this</span><span style="color: #000000;">.wait(); } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) { e.printStackTrace(); } } list.add(i); </span>++<span style="color: #000000;">count; </span><span style="color: #0000ff;">this</span>.notifyAll();<span style="color: #008000;">//</span><span style="color: #008000;">通知消費者來消費</span>
}
</span><span style="color: #008000;">//</span><span style="color: #008000;">消費者線程</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">synchronized</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> get(){ </span><span style="color: #0000ff;">while</span>(list.size()==0<span style="color: #000000;">){ </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> { </span><span style="color: #0000ff;">this</span><span style="color: #000000;">.wait(); } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) { e.printStackTrace(); } } </span><span style="color: #0000ff;">int</span> num=<span style="color: #000000;">list.removeFirst(); count</span>--<span style="color: #000000;">; </span><span style="color: #0000ff;">this</span>.notifyAll();<span style="color: #008000;">//</span><span style="color: #008000;">通知生產者生產</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> num; } </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) { MyContainerOne container</span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> MyContainerOne(); </span><span style="color: #008000;">//</span><span style="color: #008000;">制造10個消費者</span> <span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> i=0;i<10;i++<span style="color: #000000;">){ </span><span style="color: #0000ff;">new</span> Thread(()-><span style="color: #000000;">{ </span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> j=0;j<5;j++<span style="color: #000000;">) System.out.println(container.get()); }, </span>"c"+<span style="color: #000000;">i).start(); } </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> { TimeUnit.SECONDS.sleep(</span>2<span style="color: #000000;">); } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) { e.printStackTrace(); } </span><span style="color: #008000;">//</span><span style="color: #008000;">制造2個生產者</span> <span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> i=0;i<2;i++<span style="color: #000000;">){ </span><span style="color: #0000ff;">new</span> Thread(()-><span style="color: #000000;">{ </span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> j=0;j<25;j++<span style="color: #000000;">) container.put(j); }, </span>"p"+<span style="color: #000000;">i).start(); } }
}
/** * 模擬生產者消費者模式-reentrantLock-awit/signAll * @author qiuyongAaron */ public class MyContainerTwo {LinkedList</span><Integer> list=<span style="color: #0000ff;">new</span> LinkedList<Integer><span style="color: #000000;">(); </span><span style="color: #0000ff;">static</span> <span style="color: #0000ff;">final</span> <span style="color: #0000ff;">int</span> MAX=10<span style="color: #000000;">; </span><span style="color: #0000ff;">int</span> count=0<span style="color: #000000;">; ReentrantLock lock</span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> ReentrantLock(); Condition producer</span>=<span style="color: #000000;">lock.newCondition(); Condition consumer</span>=<span style="color: #000000;">lock.newCondition(); </span><span style="color: #008000;">//</span><span style="color: #008000;">生產者線程</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span> put(<span style="color: #0000ff;">int</span><span style="color: #000000;"> i){ </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> { lock.lock(); </span><span style="color: #0000ff;">while</span>(list.size()==<span style="color: #000000;">MAX){ producer.await(); } list.add(i); </span>++<span style="color: #000000;">count; consumer.signalAll();</span><span style="color: #008000;">//</span><span style="color: #008000;">通知消費者來消費</span> } <span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e){ e.printStackTrace(); }</span><span style="color: #0000ff;">finally</span><span style="color: #000000;">{ lock.unlock(); } } </span><span style="color: #008000;">//</span><span style="color: #008000;">消費者線程</span> <span style="color: #0000ff;">public</span> <span style="color: #0000ff;">int</span><span style="color: #000000;"> get(){ </span><span style="color: #0000ff;">try</span><span style="color: #000000;">{ lock.lock(); </span><span style="color: #0000ff;">while</span>(list.size()==0<span style="color: #000000;">){ consumer.await(); } </span><span style="color: #0000ff;">int</span> num=<span style="color: #000000;">list.removeFirst(); count</span>--<span style="color: #000000;">; producer.signalAll();</span><span style="color: #008000;">//</span><span style="color: #008000;">通知生產者生產</span> <span style="color: #0000ff;">return</span><span style="color: #000000;"> num; }</span><span style="color: #0000ff;">catch</span><span style="color: #000000;">(Exception e){ e.printStackTrace(); }</span><span style="color: #0000ff;">finally</span><span style="color: #000000;">{ lock.unlock(); } </span><span style="color: #0000ff;">return</span> 0<span style="color: #000000;">; } </span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> main(String[] args) { MyContainerTwo container</span>=<span style="color: #0000ff;">new</span><span style="color: #000000;"> MyContainerTwo(); </span><span style="color: #008000;">//</span><span style="color: #008000;">制造10個消費者</span> <span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> i=0;i<10;i++<span style="color: #000000;">){ </span><span style="color: #0000ff;">new</span> Thread(()-><span style="color: #000000;">{ </span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> j=0;j<5;j++<span style="color: #000000;">) System.out.println(container.get()); }, </span>"c"+<span style="color: #000000;">i).start(); } </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> { TimeUnit.SECONDS.sleep(</span>2<span style="color: #000000;">); } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) { e.printStackTrace(); } </span><span style="color: #008000;">//</span><span style="color: #008000;">制造2個生產者</span> <span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> i=0;i<2;i++<span style="color: #000000;">){ </span><span style="color: #0000ff;">new</span> Thread(()-><span style="color: #000000;">{ </span><span style="color: #0000ff;">for</span>(<span style="color: #0000ff;">int</span> j=0;j<25;j++<span style="color: #000000;">) container.put(j); }, </span>"p"+<span style="color: #000000;">i).start(); } }
}
四、版權聲明
作者:邱勇Aaron
出處:http://www.cnblogs.com/qiuyong/
您的支持是對博主深入思考總結的最大鼓勵。
本文版權歸作者所有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,尊重作者的勞動成果。
參考:馬士兵並發編程、並發編程實踐
AQS詳解:http://www.cnblogs.com/waterystone/p/4920797.html