CountDownLatch和CyclicBarrier作为协调线程运行顺序的控制机制,Semaphore一般用作资源限制访问条件。
CountDownLatch
常用方法有
<span style="font-family:FangSong_GB2312;font-size:18px;">public CountDownLatch(int count)
public void await() throws InterruptedException
public boolean await(long timeout,TimeUnit unit) throws InterruptedException
public void countDown()</span>
示例:
<span style="font-family:FangSong_GB2312;font-size:18px;">public class t{
public static void main(String[] args) throws InterruptedException{
final CountDownLatch begin=new CountDownLatch(1);//八大门派围攻光明顶,过期不候
final CountDownLatch end=new CountDownLatch(8);//撤退信号
for(int i=1;i<=8;i++){
new Thread(){
public void run(){
try{
begin.await(3L,TimeUnit.SECONDS);//等3秒,人不齐也开搞
Thread.sleep(1000);
}catch(InterruptedException e){
e.printStackTrace();
}finally{
end.countDown();//撤离通知
}
}
}.start();
}
Thread.sleep(1000);
begin.countDown();//动手信号
System.out.println("开打");
end.await(3L,TimeUnit.SECONDS);//等待撤离,阵亡不等
System.out.println("扯呼");
}
}</span>
与将lock的unlock操作放在finally中出发点相同,countDown操作也应该放在finally中,忘了就麻烦了
CyclicBarrier
构造函数
<span style="font-family:FangSong_GB2312;font-size:18px;">public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
public CyclicBarrier(int parties) {
this(parties, null);
}</span>
等待函数
<span style="font-family:FangSong_GB2312;font-size:18px;">public int await() throws InterruptedException,
BrokenBarrierException
public int await(long timeout,TimeUnit unit)
throws InterruptedException,
BrokenBarrierException,
TimeoutException</span>
对象内维持一个值parties与一个初始值count与parties相等,与CountDownLatch不同的是,CyclicBarrier的await函数使count减一,当count减为零,即getNumberWaiting()与getParties()相同,count重新赋值为parties,从而可以实现循环使用。调用带有计时功能的await函数,如果等待超时,则会抛出TimeoutException,清除等待线程的计数,并破坏对象,之前或之后其他线程中调用同一个CyclicBarrier的await(无论是否带有计时参数)都会抛出BrokenBarrierException。
示例:
<span style="font-family:FangSong_GB2312;font-size:18px;">public class t{
public static void main(String[] args) throws InterruptedException{
final CyclicBarrier cyc=new CyclicBarrier(3);//可循环使用的barrier
new Thread(){
public void run(){
try{
cyc.await(1L,TimeUnit.SECONDS);//等待一秒,继续或抛出异常
}catch(Exception e){
e.printStackTrace();
}
}
}.start();
new Thread(){
public void run(){
try{
cyc.await();//新的等待会直接抛出BrokenBarrierException异常
}catch(Exception e){
e.printStackTrace();
}
}
}.start();
Thread.sleep(100);
System.out.println("current waiting thread number: "+cyc.getNumberWaiting());//当前等待barrier线程数量
Thread.sleep(1000);
System.out.println("current waiting thread number: "+cyc.getNumberWaiting());//barrier已被破坏,等待数清零
new Thread(){ //新的等待会直接抛出BrokenBarrierException异常
public void run(){
try{
cyc.await();
}catch(Exception e){
e.printStackTrace();
}
}
}.start();
}
}</span>
当然,可以直接使用isBroken()函数进行判断
示例:
<span style="font-family:FangSong_GB2312;font-size:18px;">public class t{
public static void main(String[] args) throws ClassNotFoundException{
final Shuffle shuffle=new Shuffle();//一局结束,洗牌任务
final CyclicBarrier cyc=new CyclicBarrier(3,shuffle);
final Play play=new Play(cyc);//打牌工作
final Thread player1=new Thread(play,"张三丰");//三个线程实体
final Thread player2=new Thread(play,"张无忌");//赋予要做的工作
final Thread player3=new Thread(play,"张翠山");//就是打牌
player1.start();
player2.start();
player3.start();
}
}
class Play implements Runnable{//工作内容
private final CyclicBarrier cyc;
public Play(CyclicBarrier cyc){
this.cyc=cyc;
}
public void run(){
while(true){
try{
cyc.await();
System.out.println("\n"+Thread.currentThread().getName()+"begin playing");
Thread.sleep(2000);
}catch(Exception e){
e.printStackTrace();
}
}
}
}
class Shuffle implements Runnable{ //当多个线程达到一定条件时,任选一个线程来实现的工作,就是洗牌
public void run(){
System.out.println("-----------------------------------------------");
System.out.println(Thread.currentThread().getName()+"Shuffle the cards");
}
}</span>
运行结果:
<span style="font-family:FangSong_GB2312;font-size:18px;">-----------------------------------------------
张翠山Shuffle the cards
张翠山begin playing
张无忌begin playing
张三丰begin playing
-----------------------------------------------
张翠山Shuffle the cards
张翠山begin playing
张三丰begin playing
张无忌begin playing
-----------------------------------------------
张无忌Shuffle the cards
张无忌begin playing
张三丰begin playing
张翠山begin playing
-----------------------------------------------
张无忌Shuffle the cards
张无忌begin playing
张三丰begin playing
张翠山begin playing</span>
常用函数
<span style="font-family:FangSong_GB2312;font-size:18px;">public void acquire() throws InterruptedException
public void acquire(int permits) throws InterruptedException
public void release()
public void release(int permits)</span>
acquire发出获取资源需求,当现有资源数大于等于需求时,顺利执行,否则阻塞等待,可相应中断,其他函数像tryAcquire参考lock中的tryLock操作,公平或者不公平的构造Semaphore影响acquire成功的顺序。
示例:
<span style="font-family:FangSong_GB2312;font-size:18px;">public class t{
public static void main(String[] args) throws InterruptedException{
final Semaphore sem=new Semaphore(5);
for(int i=0;i<4;i++){
new Thread(){
public void run(){
try{
sem.acquire(2);//占据两个
Thread.sleep(1000);
System.out.println(Thread.currentThread().getId()+" get");
}catch(InterruptedException e){
e.printStackTrace();
}finally{
sem.release(1);//释放一个
}
}
}.start();
}
}
}</span>
总结
CountDownLatch、CyclicBarrier都可实现对线程运行顺序的调节,门闩侧重于一个线程发出通知,唤醒其他的多个等待线程同时执行(当然自身也会继续执行),栅栏侧重多个线程通过自身的努力,完成了条件可以继续执行,并且栅栏可以重复使用。Semaphore主要是控制同时运行的线程数量,能够在系统资源和线程数量之间进行调节,提高效率,避免死锁。