并发编程(三):从AQS到CountDownLatch与ReentrantLock
一、目录
二、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