【小家java】JUC并发编程工具之CountDownLatch(闭锁)、CyclicBarrier、Semaphore的使用(下)

简介: 【小家java】JUC并发编程工具之CountDownLatch(闭锁)、CyclicBarrier、Semaphore的使用(下)

CyclicBarrier和CountDownLatch的区别


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


   CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得CyclicBarrier阻塞的线程数量。isBroken方法用来知道阻塞的线程是否被中断。比如以下代码执行完之后会返回true。


   CountDownLatch是减计数方式,而CyclicBarrier是加计数方式


   CountDownLatch不可以复用,而CyclicBarrier可以复用。


Semaphore


Semaphore是一种计数信号量,用于管理一组资源,内部是基于AQS的共享模式。它相当于给线程规定一个量从而控制允许活动的线程数。


Semaphore是一种在多线程环境下使用的设施,该设施负责协调各个线程,以保证它们能够正确、合理的使用公共资源的设施,也是操作系统中用于控制进程同步互斥的量。

原来阐述


以一个停车场为例。加入一共只有3个停车位,那么当来了5辆车的时候,那么看门的人可以不受阻碍的放3辆车进去,其余的在入口处等候。这时候如果继续来车就都得在门外等候。


这时,如果有一辆车停车位里的车离开了,就可以放一辆进来了(至于放哪俩进来,有公平锁和非公平锁之分),如此往复。


每辆车就好比一个线程,看门人就好比一个信号量,看门人限制了可以活动的线程。

对于Semaphore类而言,就如同一个看门人,限制了可活动的线程数。


主要方法:

Semaphore(int permits):构造方法,创建具有给定许可数的计数信号量并设置为非公平信号量。
Semaphore(int permits,boolean fair):构造方法,当fair等于true时,创建具有给定许可数的计数信号量并设置为公平信号量。
void acquire():从此信号量获取一个许可前线程将一直阻塞。相当于一辆车占了一个车位。
void acquire(int n):从此信号量获取给定数目许可,在提供这些许可前一直将线程阻塞。比如n=2,就相当于一辆车占了两个车位。
void release():释放一个许可,将其返回给信号量。就如同车开走返回一个车位。
void release(int n):释放n个许可。
int availablePermits():当前可用的许可数。


代码示例


就以上面的提车系统,用代码里实现(仅供参考)


    //控制三个停车位
    private static final Semaphore semaphore = new Semaphore(3);
    //线程池:核心线程数可以有5个
    private static final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
            5, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
    //一辆车代表一个进程 进去提车系统
    private static class CarThread extends Thread {
        private final String name; //品牌名称
        private final int age; //使用了多少年
        public CarThread(String name, int age) {
            this.name = name;
            this.age = age;
        }
        @Override
        public void run() {
            try {
                semaphore.acquire(); //常识去获取许可 进入停车
                System.out.println(Thread.currentThread().getName() + ":大家好,我是【" + name + "】使用了【" + age + "】年,当前时间为:" + System.currentTimeMillis());
                Thread.sleep(1000); //模拟停车时长 停后离开
                System.out.println("【" + name + "】要准备离开停车场了,当前剩余空位【" + semaphore.availablePermits() + "】,当前时间为:" + System.currentTimeMillis());
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        String[] name = {"奔驰", "宝马", "奥迪", "大众", "尼桑", "wey派", "领克"};
        int[] age = {1, 3, 5, 6, 7, 10, 12};
        //一次性来了7辆车
        for (int i = 0; i < 7; i++) {
            threadPool.execute(new CarThread(name[i], age[i]));
        }
    }
输出:
pool-1-thread-3:大家好,我是【奥迪】使用了【5】年,当前时间为:1544953335062
pool-1-thread-2:大家好,我是【宝马】使用了【3】年,当前时间为:1544953335062
pool-1-thread-1:大家好,我是【奔驰】使用了【1】年,当前时间为:1544953335063
【奔驰】要准备离开停车场了,当前剩余空位【0】,当前时间为:1544953336081
pool-1-thread-1:大家好,我是【wey派】使用了【10】年,当前时间为:1544953336081
【奥迪】要准备离开停车场了,当前剩余空位【0】,当前时间为:1544953336085
pool-1-thread-4:大家好,我是【大众】使用了【6】年,当前时间为:1544953336088
【宝马】要准备离开停车场了,当前剩余空位【0】,当前时间为:1544953336088
pool-1-thread-5:大家好,我是【尼桑】使用了【7】年,当前时间为:1544953336089
【wey派】要准备离开停车场了,当前剩余空位【0】,当前时间为:1544953337083
pool-1-thread-3:大家好,我是【领克】使用了【12】年,当前时间为:1544953337083
【大众】要准备离开停车场了,当前剩余空位【0】,当前时间为:1544953337089
【尼桑】要准备离开停车场了,当前剩余空位【1】,当前时间为:1544953337094
【领克】要准备离开停车场了,当前剩余空位【2】,当前时间为:1544953338083
private static final Semaphore semaphore = new Semaphore(3, true);


运行:


输出:
pool-1-thread-2:大家好,我是【宝马】使用了【3】年,当前时间为:1544953560457
pool-1-thread-3:大家好,我是【奥迪】使用了【5】年,当前时间为:1544953560457
pool-1-thread-1:大家好,我是【奔驰】使用了【1】年,当前时间为:1544953560457
【奔驰】要准备离开停车场了,当前剩余空位【0】,当前时间为:1544953561468
pool-1-thread-4:大家好,我是【大众】使用了【6】年,当前时间为:1544953561468
【奥迪】要准备离开停车场了,当前剩余空位【0】,当前时间为:1544953561468
pool-1-thread-5:大家好,我是【尼桑】使用了【7】年,当前时间为:1544953561468
【宝马】要准备离开停车场了,当前剩余空位【0】,当前时间为:1544953561469
pool-1-thread-1:大家好,我是【wey派】使用了【10】年,当前时间为:1544953561469
【大众】要准备离开停车场了,当前剩余空位【0】,当前时间为:1544953562468
pool-1-thread-3:大家好,我是【领克】使用了【12】年,当前时间为:1544953562468
【尼桑】要准备离开停车场了,当前剩余空位【0】,当前时间为:1544953562469
【wey派】要准备离开停车场了,当前剩余空位【0】,当前时间为:1544953562469
【领克】要准备离开停车场了,当前剩余空位【2】,当前时间为:1544953563470


从输出的结果可以看出"大众", “尼桑”, “wey派”, "领克"是排队按照顺序进入的,这时候就是公平锁了。


Semaphore内部基于AQS的共享模式,所以实现都委托给了Sync类。 (原理其实大家可以自行参照源码,也比较简单)


new Semaphore(1)可以利用这个,间接实现单例模式


Semaphore总结


Semaphore主要用于控制当前活动线程数目,就如同停车场系统一般,而Semaphore则相当于看守的人,用于控制总共允许停车的停车位的个数。虽然我们自己也可以通过lock等手动来控制,但既然JUC为我们提供了便捷的工具,为何不使用呢?


Semaphore其实和锁有点类似,它一般用于控制对某组资源的访问权限。


相关面试题


解释一下CountDownLatch概念?


CountDownLatch 和CyclicBarrier的不同之处?


给出一些CountDownLatch使用的例子?


CountDownLatch 类中主要的方法?


说说你对Semaphore的理解,可以写一个示例吗?

相关文章
|
4月前
|
Java 编译器 开发者
深入理解Java内存模型(JMM)及其对并发编程的影响
【9月更文挑战第37天】在Java的世界里,内存模型是隐藏在代码背后的守护者,它默默地协调着多线程环境下的数据一致性和可见性问题。本文将揭开Java内存模型的神秘面纱,带领读者探索其对并发编程实践的深远影响。通过深入浅出的方式,我们将了解内存模型的基本概念、工作原理以及如何在实际开发中正确应用这些知识,确保程序的正确性和高效性。
|
2月前
|
安全 Java 程序员
深入理解Java内存模型与并发编程####
本文旨在探讨Java内存模型(JMM)的复杂性及其对并发编程的影响,不同于传统的摘要形式,本文将以一个实际案例为引子,逐步揭示JMM的核心概念,包括原子性、可见性、有序性,以及这些特性在多线程环境下的具体表现。通过对比分析不同并发工具类的应用,如synchronized、volatile关键字、Lock接口及其实现等,本文将展示如何在实践中有效利用JMM来设计高效且安全的并发程序。最后,还将简要介绍Java 8及更高版本中引入的新特性,如StampedLock,以及它们如何进一步优化多线程编程模型。 ####
50 0
|
5月前
|
安全 Java API
JAVA并发编程JUC包之CAS原理
在JDK 1.5之后,Java API引入了`java.util.concurrent`包(简称JUC包),提供了多种并发工具类,如原子类`AtomicXX`、线程池`Executors`、信号量`Semaphore`、阻塞队列等。这些工具类简化了并发编程的复杂度。原子类`Atomic`尤其重要,它提供了线程安全的变量更新方法,支持整型、长整型、布尔型、数组及对象属性的原子修改。结合`volatile`关键字,可以实现多线程环境下共享变量的安全修改。
|
3月前
|
缓存 Java 开发者
Java多线程并发编程:同步机制与实践应用
本文深入探讨Java多线程中的同步机制,分析了多线程并发带来的数据不一致等问题,详细介绍了`synchronized`关键字、`ReentrantLock`显式锁及`ReentrantReadWriteLock`读写锁的应用,结合代码示例展示了如何有效解决竞态条件,提升程序性能与稳定性。
354 6
|
4月前
|
存储 消息中间件 安全
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
【10月更文挑战第9天】本文介绍了如何利用JUC组件实现Java服务与硬件通过MQTT的同步通信(RRPC)。通过模拟MQTT通信流程,使用`LinkedBlockingQueue`作为消息队列,详细讲解了消息发送、接收及响应的同步处理机制,包括任务超时处理和内存泄漏的预防措施。文中还提供了具体的类设计和方法实现,帮助理解同步通信的内部工作原理。
JUC组件实战:实现RRPC(Java与硬件通过MQTT的同步通信)
|
3月前
|
存储 缓存 安全
Java内存模型(JMM):深入理解并发编程的基石####
【10月更文挑战第29天】 本文作为一篇技术性文章,旨在深入探讨Java内存模型(JMM)的核心概念、工作原理及其在并发编程中的应用。我们将从JMM的基本定义出发,逐步剖析其如何通过happens-before原则、volatile关键字、synchronized关键字等机制,解决多线程环境下的数据可见性、原子性和有序性问题。不同于常规摘要的简述方式,本摘要将直接概述文章的核心内容,为读者提供一个清晰的学习路径。 ####
60 2
|
3月前
|
设计模式 安全 Java
Java 多线程并发编程
Java多线程并发编程是指在Java程序中使用多个线程同时执行,以提高程序的运行效率和响应速度。通过合理管理和调度线程,可以充分利用多核处理器资源,实现高效的任务处理。本内容将介绍Java多线程的基础概念、实现方式及常见问题解决方法。
193 0
|
5月前
|
存储 缓存 安全
【Java面试题汇总】多线程、JUC、锁篇(2023版)
线程和进程的区别、CAS的ABA问题、AQS、哪些地方使用了CAS、怎么保证线程安全、线程同步方式、synchronized的用法及原理、Lock、volatile、线程的六个状态、ThreadLocal、线程通信方式、创建方式、两种创建线程池的方法、线程池设置合适的线程数、线程安全的集合?ConcurrentHashMap、JUC
|
5月前
|
Java 开发者
深入探索Java中的并发编程
本文将带你领略Java并发编程的奥秘,揭示其背后的原理与实践。通过深入浅出的解释和实例,我们将探讨Java内存模型、线程间通信以及常见并发工具的使用方法。无论是初学者还是有一定经验的开发者,都能从中获得启发和实用的技巧。让我们一起开启这场并发编程的奇妙之旅吧!
45 5
|
5月前
|
算法 安全 Java
Java中的并发编程是如何实现的?
Java中的并发编程是通过多线程机制实现的。Java提供了多种工具和框架来支持并发编程。
30 1