CountDownLatch&CyclicBarrier&Semaphore

CountDownLatch&CyclicBarrier&Semaphore

  • CountDownLatch和CyclicBarrier都能够实现线程之间的等待,但侧重点略微不同
    • CountDownLatch一般用于某个线程等待若干个其他线程执行完任务之后它才执行
    • CyclicBarrier一般用于一组线程相互等待至某个状态,然后这一组线程再同时执行
    • 而且CountDownLatch是使用AQS框架实现的,而CyclicBarrier使用ReentrantLock实现的
  • Semaphore和锁有点类似,它一般用于控制对一组资源的访问权限,它也是使用AQS框架实现的,并且内部有两种模式,一种是FairSync,代表让先进入队列中的等待任务获取一个许可,第二种是NonFairSync,代表不管是不是先进入队列中的,可以直接去获取许可

CountDownLatch

  • CountDownLatch是在java1.5被引入的,跟它一起引入的工具类还有CyclicBarrier、Semaphore、ConcurrentHashmap和BlockingQueue

  • CountDownLathc使一个线程等待其他线程各自执行完毕后再执行

  • 它是通过一个计数器实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后再闭锁上等待的线程就可以恢复工作了。

源码分析

  • CountDownLatch类只提供了一个构造器
//参数count为计数值
//CountDownLatch也是基于AQS框架的,这里的count其实就是赋给AQS的state
public CountDownLatch(int count) {  };  
  • 类中有三个方法很重要
//调用await方法的线程会被挂起,它会等待直到count值为0时才继续执行
public void await() throws InterruptedException{};

//和await方法类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
public boolean await(long timeout,TimeUnit unit) throws InterruptedException{};

//count-1
public void countDown(){};
  • 普通示例
    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(2);
        System.out.println("主线程开始执行…… ……");
        //第一个子线程执行
        EXECUTOR_SERVICE.execute(()->{
            try {
                Thread.sleep(3000);
                System.out.println("子线程:"+Thread.currentThread().getName()+"执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            countDownLatch.countDown();
        });

        //第二个子线程执行
        EXECUTOR_SERVICE.execute(()->{
            try {
                Thread.sleep(3000);
                System.out.println("子线程:"+Thread.currentThread().getName()+"执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            countDownLatch.countDown();
        });

        System.out.println("等待两个线程执行完毕…… ……");
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("两个子线程都执行完毕,继续执行主线程");
    }
    
  • 结果如下:
主线程开始执行…… ……
等待两个线程执行完毕…… ……
子线程:mycountdownlatch-pool-0执行
子线程:mycountdownlatch-pool-1执行
两个子线程都执行完毕,继续执行主线程
  • 并发示例
public class MyCountDownLatch {
    private static final ThreadPoolExecutor EXECUTOR_SERVICE = new ThreadPoolExecutor(100,120,60,TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(1000),
            new ThreadFactoryBuilder().setNameFormat("mycountdownlatch-pool-%d").build(),
            new ThreadPoolExecutor.CallerRunsPolicy());

    public static void main(String[] args) {
        CountDownLatch latch = new CountDownLatch(100);
        for(int i=0;i<100;i++){
            CountRunnable runnable = new CountRunnable(latch);
            EXECUTOR_SERVICE.execute(runnable);
        }
    }
}
class CountRunnable implements Runnable{
    private CountDownLatch latch = new CountDownLatch(100);
    public CountRunnable(CountDownLatch latch){
        this.latch = latch;
    }

    @Override
    public void run() {
        try{
            synchronized (latch){
                latch.countDown();
                System.out.println("thread counts = " + (latch.getCount()));
            }
            latch.await();
            System.out.println("concurrency counts = " + (100 - latch.getCount()));
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

CyclicBarrier

  • 中文意思是回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。当调用await方法之后,线程就处于barrier了。

源码分析

//parties指让多少个线程或者任务等待值barrier状态
//参数BarrierAction为这些线程都达到barrier状态时会执行的内容
public CyclicBarrier(int parties, Runnable barrierAction) {
}
 
public CyclicBarrier(int parties) {
}
//挂起当前线程,直到所有线程都达到barrier状态再同时执行后续任务
public int await() throws InterruptedException, BrokenBarrierException { };
//让这些线程等待至一定的时间,如果还有线程没有到达barrier状态就直接让到达barrier的线程执行后续任务。
public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };

示例代码

  • 示例代码如下
private static final ThreadPoolExecutor EXECUTOR_SERVICE = new ThreadPoolExecutor(100,120,60, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(1000),
            new ThreadFactoryBuilder().setNameFormat("mycyclicbarrier-pool-%d").build(),
            new ThreadPoolExecutor.CallerRunsPolicy());
public static void main(String[] args) {
    CyclicBarrier barrier = new CyclicBarrier(4);
    for(int i=0;i<4;i++){
        EXECUTOR_SERVICE.execute(()->{
            System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
            try {
                Thread.sleep(5000);      //以睡眠来模拟写入数据操作
                System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");
                barrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }catch(BrokenBarrierException e){
                e.printStackTrace();
            }
            System.out.println("所有线程写入完毕,继续处理其他任务...");
        });
    }
}
  • 结果如下
线程mycyclicbarrier-pool-0正在写入数据...
线程mycyclicbarrier-pool-3正在写入数据...
线程mycyclicbarrier-pool-2正在写入数据...
线程mycyclicbarrier-pool-1正在写入数据...
线程mycyclicbarrier-pool-0写入数据完毕,等待其他线程写入完毕
线程mycyclicbarrier-pool-2写入数据完毕,等待其他线程写入完毕
线程mycyclicbarrier-pool-3写入数据完毕,等待其他线程写入完毕
线程mycyclicbarrier-pool-1写入数据完毕,等待其他线程写入完毕
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...

Semaphore

  • 字面意思信号量,Semaphore可以控制同时访问的线程个数,通过acquire()获取一个许可,如果没有就等待,而release()释放一个许可

源码分析

public Semaphore(int permits) {          //参数permits表示许可数目,即同时可以允许多少线程进行访问
    sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {    //这个多了一个参数fair表示是否是公平的,即等待时间越久的越先获取许可
    sync = (fair)? new FairSync(permits) : new NonfairSync(permits);
}

//获取一个许可,若无许可则会一直等待,直到获取许可
public void acquire() throws InterruptedException {  }     
//获取permits许可
public void acquire(int permits) throws InterruptedException { }
//释放一个许可
public void release() { }   
//释放permits个许可
public void release(int permits) { }    

示例代码

private static final ThreadPoolExecutor EXECUTOR_SERVICE = new ThreadPoolExecutor(100,120,60, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(1000),
            new ThreadFactoryBuilder().setNameFormat("mycyclicbarrier-pool-%d").build(),
            new ThreadPoolExecutor.CallerRunsPolicy());
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(5);
        for(int i=0;i<8;i++){
            EXECUTOR_SERVICE.execute(new SemaphoreRunnable(i,semaphore));
        }
    }
    
class SemaphoreRunnable implements Runnable{
    int num;
    Semaphore semaphore;
    public SemaphoreRunnable(int num,Semaphore semaphore){
        this.num = num;
        this.semaphore = semaphore;
    }

    @Override
    public void run() {
        try {
            semaphore.acquire();
            System.out.println("工人"+this.num+"占用一个机器在生产...");
            Thread.sleep(2000);
            System.out.println("工人"+this.num+"释放出机器");
            semaphore.release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
  • 结果如下:
工人0占用一个机器在生产...
工人1占用一个机器在生产...
工人2占用一个机器在生产...
工人3占用一个机器在生产...
工人4占用一个机器在生产...
工人1释放出机器
工人4释放出机器
工人5占用一个机器在生产...
工人6占用一个机器在生产...
工人0释放出机器
工人2释放出机器
工人3释放出机器
工人7占用一个机器在生产...
工人7释放出机器
工人5释放出机器
工人6释放出机器
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值