简析CountDownLatch、CyclicBarrier、Semaphore

目录

一、CountDownLatch

1、定义

2、应用场景

3、程序代码 

二、CyclicBarrier

1、定义

 2、应用场景

 3、程序代码

三、CountDownLatch与CyclicBarrier 区别

四、Semaphore

1、定义

2、应用场景

3、程序代码


一、CountDownLatch

1、定义

用来协调线程间的同步,起到线程之间的通信作用的工具类。它主要又两个方法,当一个或多个线程调用await方法时,调用线程会被阻塞。其他线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞),当计数器的值变为0时,因调用await方法被阻塞的线程会被唤醒,继续执行。

        它能够使一个线程再等待一些线程完成各自工作后,再继续执行。使用一个计数器实现,计数器初始值为线程的数量,当每一个线程完成自己任务后,计数器的值会减1,当计数器的值为0时,表示所有的线程都完成了任务,然后CountDownLatch上等待的线程就可以恢复执行任务。

2、应用场景

  • 某一个线程再开始运行前等待n个线程执行完毕,如启动一个服务时,主线程需要等待多个组件加载完毕,之后再执行。
  • 实现多个线程开始执行任务的最大并行性,注意是并行性,不是并发,强调的是多个线程在某一时刻同时开始执行。做法是初始化一个共享的CountDownLatch(1),将其计数器初始化为1,多个线程在开始执行任务前首先 coundownlatch.await(),让所有线程都在这个锁上等待,当主线程调用 countDown() 时,计数器变为0,多个线程同时被唤醒。

3、程序代码 

        CountDownLatch是一次性的,计数器的值只能再构造方法中初始化一次,之后没有任何机制再次对其设置值,当CountDownLatch使用完毕后,它不能再次被使用。 

package com.study.thread;

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {

    private static CountDownLatch countDownLatch = new CountDownLatch(7);

    private void runCountDown() {
        countDownLatch.countDown();
        System.out.println(Thread.currentThread().getName() + "课程通过");
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 7; i++) {
            new Thread(() -> {
                CountDownLatchDemo demo = new CountDownLatchDemo();
                demo.runCountDown();
            }, i + "").start();
        }
        countDownLatch.await();
        new Thread(() -> {
            System.out.println("考试通过");
        }, "BB").start();
    }
}

运行结果:

0课程通过
5课程通过
2课程通过
6课程通过
3课程通过
4课程通过
1课程通过
考试通过

二、CyclicBarrier

1、定义

        字面意思是可循环(Cyclic)使用的屏障(Barrier),它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活,线程进入屏障通过CyclicBarrier的await()方法。

        CyclicBarrier类是concurrent并发包下的一工具类。
        线程间同步阻塞是使用的是ReentrantLock,可重入锁 

 2、应用场景

  • CyclicBarrier可以用于多线程计算数据,最后合并计算结果的应用场景

 3、程序代码

package com.study.thread;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
            System.out.println("收集完成,兑换大奖");
        });
        for (int i = 0; i < 7; i++) {
            new Thread(() -> {
                System.out.println("收集卡片" + Thread.currentThread().getName());
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }, i + "").start();
        }
    }
}

运行结果

收集卡片0
收集卡片6
收集卡片5
收集卡片4
收集卡片3
收集卡片2
收集卡片1
收集完成,兑换大奖

三、CountDownLatch与CyclicBarrier 区别

1、CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次

2、CyclicBarrier还提供getNumberWaiting(可以获得CyclicBarrier阻塞的线程数量)、isBroken(用来知道阻塞的线程是否被中断)等方法。


3、CountDownLatch会阻塞主线程,CyclicBarrier不会阻塞主线程,只会阻塞子线程。

四、Semaphore

1、定义

        信号量主要用于两个目的,一个是用于多个共享资源的互斥使用(单个信号量的Semaphore对象可以实现互斥锁的功能),另一个用于并发线程数的控制,synchronized和重入锁ReentrantLock,这2种锁一次都只能允许一个线程访问一个资源,而信号量可以控制有多少个线程可以访问特定的资源。

        Semaphore内部维护了一组虚拟的许可,许可的数量可以通过构造函数的参数指定。

  • 访问特定资源前,必须使用acquire方法获得许可,如果许可数量为0,该线程则一直阻塞,直到有可用许可。
  • 访问资源后,使用release释放许可。
  • Semaphore和ReentrantLock类似,获取许可有公平策略和非公平许可策略,默认情况下使用非公平策略

2、应用场景

        Semaphore可以用来做流量分流,特别是对公共资源有限的场景,比如数据库连接。
假设有这个的需求,读取几万个文件的数据到数据库中,由于文件读取是IO密集型任务,可以启动几十个线程并发读取,但是数据库连接数只有10个,这时就必须控制最多只有10个线程能够拿到数据库连接进行操作。这个时候,就可以使用Semaphore做流量控制。

3、程序代码

package com.study.thread;

import java.util.Arrays;
import java.util.concurrent.Semaphore;

public class SemaphoreDemo {

    public static void main(String[] args) {
        //数据库连接数只有3个
        Semaphore semaphore = new Semaphore(3);
        //模拟有7个文件
        for (int i = 0; i < 7; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + "获取数据库连接");
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    System.out.println(Thread.currentThread().getName() + ">>>>>>释放数据库连接");
                    semaphore.release();
                }
            }, i + "").start();
        }
    }
}

运行结果: 

0获取数据库连接
1获取数据库连接
2获取数据库连接
0>>>>>>释放数据库连接
2>>>>>>释放数据库连接
1>>>>>>释放数据库连接
3获取数据库连接
5获取数据库连接
4获取数据库连接
3>>>>>>释放数据库连接
5>>>>>>释放数据库连接
4>>>>>>释放数据库连接
6获取数据库连接
6>>>>>>释放数据库连接



 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值