CountDownLatch、CyclicBarrier和Semaphore

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>


在CountDownLatch中维持一个计数,countDown()使计数减一(一个线程内可以调用多次countDown操作,直到减为零,反正不会抛出异常),计数为零或者await(timeout,unit)操作等待超时,则调用await的阻塞线程恢复执行。从字面意思可以看出latch作为门闩,作用是阻塞多个线程的执行和通知多个线程的执行。
示例:

<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()函数进行判断


CyclicBarrier从名字中可以看出是栅栏的意思,作用是阻拦多个线程和等待多个线程的同时执行

示例:

<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>


Semaphore

常用函数

<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>


通过调节release的参数,可以控制执行时间或者发生死锁,release操作仍然推荐放在finally中。当acquire和release的参数相同并且小于系统资源数时,除非某个线程因为程序结构发生死锁,否则不会因为资源问题发生死锁,这也说明了获得信号量的操作是一个具有同步语义的操作,不会发生哲学家就餐问题,在acquire返回成功之前,不会持续占据资源。

总结

CountDownLatch、CyclicBarrier都可实现对线程运行顺序的调节,门闩侧重于一个线程发出通知,唤醒其他的多个等待线程同时执行(当然自身也会继续执行),栅栏侧重多个线程通过自身的努力,完成了条件可以继续执行,并且栅栏可以重复使用。Semaphore主要是控制同时运行的线程数量,能够在系统资源和线程数量之间进行调节,提高效率,避免死锁。






  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值