CountDownLatch&CyclicBarrier&Semaphore

并发包

java中的并发包指的就是java.util.concurrent—>JUC

CountDownLatch


CountDownLatch:是java.util.concurrent并发包下的一个工具类,其意思“倒计时门闩”,它允许一个或多个线程一直等待直到其他线程执行完毕才开始执行


countDown方法和await方法,CountDownLatch在初始化时,需要指定用给定一个整数作为计数器。当调用countDown方法时,计数器会被减1;当调用await方法时,如果计数器大于0时,线程会被阻塞,一直到计数器被countDown方法减到0时,线程才会继续执行。计数器是无法重置的,当计数器被减到0时,调用await方法都会直接返回。


不足
1)CountDownLatch是不能够重用的
根据上面的解析,大家也发现,共享锁加锁的操作并不会增加state的值。CountDownLatch中state一旦变成0就没有提供其他方式增长回去了。
所以CountDownLatch是一次性消耗品,用完就得换新的。这样设计也是比较正常的,对状态的要求比较严格,例如已经开始比赛了你再告诉裁判说,这次再加5个人,比赛都进行一半了,不会考虑再回去的。随意修改约束值会带来很多逻辑上的问题。

2)CountDownLatch无限等待
countDown没有被调用,那么await就会一直等下去。countDown常见没有被调用的情况:异常中断,线程池拒绝策略。
可以使用:public boolean await(long timeout, TimeUnit unit)
根据业务情况,增加一个最大等待时间。使用这种方式,需要对失败的各种情况作出业务上的对应处理,否则就出现各种数据不正确的问题


例子:

package com.rj.bd.threads.thread19;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @desc   4名运动员等待口令(用mian线程充当裁判,新建的4个线程作为运动员)
 * @time   2019-10-22
 */
public class Test02 {
	public static void main(String[] args) {
		   //1.开启一个可缓存的线程池
		    ExecutorService service = Executors.newCachedThreadPool();
		    //2.实例化一个裁判计数器
	        final CountDownLatch cdOrder = new CountDownLatch(1);
	        //3.实例化一个远动员计数器
	        final CountDownLatch cdAnswer = new CountDownLatch(4);
	        //4.创建4个子线程用来模拟4个运动员
	        for (int i = 0; i < 4; i++) 
	        {
	            Runnable runnable = new Runnable() 
	            {
	                @Override
	                public void run() 
	                {
	                    try 
	                    {
	                    	//5输出选手正在等待裁判发布口令
	                        System.out.println("选手" + Thread.currentThread().getName() + "正在等待裁判发布口令");
	                         //7裁判计数器的等待阻塞
	                        cdOrder.await();
	                        //9.选手接受裁判口令
	                        System.out.println("选手" + Thread.currentThread().getName() + "已接受裁判口令");
	                        Thread.sleep((long) (Math.random() * 1000));
	                        //10.选手达到终点
	                        System.out.println("选手" + Thread.currentThread().getName() + "到达终点");
	                        //11.运动员计数器减为0,然后程序执行到第12步
	                        cdAnswer.countDown();
	                    } 
	                    catch (InterruptedException e) 
	                    {
	                        System.out.println("中断异常......");
	                    }
	                }
	            };
	            service.execute(runnable);
	        }
	        try {
	        	//6.让裁判计数器的计数减去1,目的是为了为了让第7步的裁判计数器阻塞等待实现放行
	            Thread.sleep((long) (Math.random() * 1000));
	            System.out.println("裁判"+Thread.currentThread().getName()+"即将发布口令");
	            cdOrder.countDown();//裁判计数器减去1
	            //8.虽然计数器会实现线程的阻塞,但是其他的正常的代码还是按照默认的从上向下的顺序执行
	            //而此时执行到55行的运动员计数器的时候,程序会发现运动员计数器的计数还不是0,于是就重新去执行运动员计数器所监控的线程的任务其实就是第9,10,11步
	            cdAnswer.await();
	            //12.主线程输出提示语:所有选手都到达终点和汇总成绩排名
	            System.out.println("所有选手都到达终点");
	            System.out.println("裁判"+Thread.currentThread().getName()+"汇总成绩排名");
	        } catch (InterruptedException e) {
	            e.printStackTrace();
	        }
	        service.shutdown();
	}

}

CyclicBarrier


CyclicBarrier:翻译成中文就是”循环栅栏/障碍“,可以使一定数量的线程反复地在栅栏位置处汇集。当线程到达栅栏位置时将调用await方法,这个方法将阻塞直到所有线程都到达栅栏位置。如果所有线程都到达栅栏位置,那么栅栏将打开,此时所有的线程都将被释放,而栅栏将被重置以便下次使用。


例子:

package com.rj.bd.threads.thread20;
/**
 * @desc   乘客类
 
 * @time   2019-10-23
 */
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;


public class Tourist extends Thread {

	private CyclicBarrier cyclicBarrier;
	
	public Tourist(CyclicBarrier cyclicBarrier){
		 this.cyclicBarrier=cyclicBarrier;
	}

	@Override
	public void run()
	{
	 
	 try {
		 System.out.println("乘客:"+getName()+"已经向巴士车走来");
		 Thread.sleep(2000);
		 System.out.println("乘客:"+getName()+"已经在巴士上坐下了");
	   } catch (InterruptedException e) 
	   {
		 System.out.println("中断异常。。。。");
	   }
		 try 
		 {
			 cyclicBarrier.await();
			 System.out.println("乘客:"+getName()+":人满发车喽。。。。。。");
		 }
		 catch (InterruptedException e) 
		 {
			System.out.println("中断异常。。。。");
		 } 
		 catch (BrokenBarrierException e) 
		 {
			System.out.println("打破障碍异常(人没到齐非要走).....");
		 }
	}
}




package com.rj.bd.threads.thread20;

/**
 * @desc   测试类:测试CyclicBarrier

 * @time   2019-10-23
 */
import java.util.concurrent.CyclicBarrier;

public class Test {
	public static void main(String[] args) {
        /**
         * CyclicBarrier的实例化:
         * 参数1:parties: 是参与线程的个数
         * 参数2:最后一个到达线程要做的任务
         */
		CyclicBarrier cyclicBarrier=new CyclicBarrier(5,new Runnable() 
		 {
			@Override
			public void run()
			{
				System.out.println("\n最后一个上车的关闭车门。。。。\n");
			}
		});
		for (int i = 0; i < 5; i++) 
		{
			Tourist writer = new Tourist(cyclicBarrier);
			writer.start();
		}
		
	}

}

Semaphore


Semaphore : 是一种基于计数的信号量。它可以设定一个阈值,基于此,多个线程竞争获取许可信号,做自己的申请后归还,超过阈值后,线程申请许可信号将会被阻塞。 Semaphore可以用来构建一些对象池,资源池之类的,比如数据库连接池,我们也可以创建计数为1的Semaphore,将其作为一种类似互斥锁的机制,这也叫二元信号量,表示两种互斥状态


简单来说:

  1. Semaphoresynchronized 的加强版,作用是控制线程的并发数量
  2. Seampore类初始化的时候就可以指定阈值(这个阈值就是线程的并发数量)
  3. Seampore经常被用来构建例如资源池,连接池这样的东西(为什么呢。因为这些池是需要一个动态平衡的)
  4. Seampore的阈值为1的时候其功能类似于互斥锁

例子:

package com.rj.bd.threads.thread20;
/**
 * @desc    测试类:测试信号量类Semapore

 * @time    2019-10-23
 */
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

public class Test03 {
	public static void main(String[] args) {
		  //1.创建一个线程池
		  ExecutorService executorService = Executors.newCachedThreadPool();
	        
	        //2.实例化信号量对象,且只允许 10个线程同时访问
	        final Semaphore semaphore = new Semaphore(10);

	        for (int i=0;i<30;i++)//3.假设程序中有30个进行IO读写的线程
	        {
	        	//4.将创建的子线程装入到线程池中并执行
	            executorService.submit(new Runnable() 
	            {
	                @Override
	                public void run() 
	                {
	                    try 
	                    {
	                        //5.获取许可
	                        semaphore.acquire();
	                        //6.执行
	                        System.out.println("\n获取许可并执行的线程的名字: " + Thread.currentThread().getName());
	                        Thread.sleep(new Random().nextInt(5000)); // 模拟随机执行时长
	                        //7.释放
	                        semaphore.release();
	                        System.out.println("\n释放线程的名字..." + Thread.currentThread().getName());
	                    } catch (InterruptedException e) 
	                    {
	                        e.printStackTrace();
	                    }
	                }
	            },i);
	        }
             //8.关闭线程池
	        executorService.shutdown();
	}

}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值