JUC面试(七)——CountDownLatchCyclicBarrierSemaphore

news2024/9/25 21:27:30

CountDownLatch

概念

让一些线程阻塞直到另一些线程完成一系列操作才被唤醒

CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,调用线程就会被阻塞。其它线程调用CountDown方法会将计数器减1(调用CountDown方法的线程不会被阻塞),当计数器的值变成零时,因调用await方法被阻塞的线程会被唤醒,继续执行

场景

现在有这样一个场景,假设一个自习室里有7个人,其中有一个是班长,班长的主要职责就是在其它6个同学走了后,关灯,锁教室门,然后走人,因此班长是需要最后一个走的,那么有什么方法能够控制班长这个线程是最后一个执行,而其它线程是随机执行的

解决方案

这个时候就用到了CountDownLatch,计数器了。我们一共创建6个线程,然后计数器的值也设置成6

// 计数器
CountDownLatch countDownLatch = new CountDownLatch(6);

然后每次学生线程执行完,就让计数器的值减1

for (int i = 1; i <= 6; i++) {
    new Thread(() -> {
        System.out.println(Thread.currentThread().getName() + "\t 上完自习,离开教室");
        countDownLatch.countDown();
    }, String.valueOf(i)).start();
}

最后我们需要通过CountDownLatch的await方法来控制班长主线程的执行,这里 countDownLatch.await()可以想成是一道墙,只有当计数器的值为0的时候,墙才会消失,主线程才能继续往下执行

countDownLatch.await();

System.out.println(Thread.currentThread().getName() + "\t 班长最后关门");

不加CountDownLatch的执行结果,我们发现main线程提前已经执行完成了

1	 上完自习,离开教室
main	 班长最后关门
2	 上完自习,离开教室
3	 上完自习,离开教室
4	 上完自习,离开教室
5	 上完自习,离开教室
6	 上完自习,离开教室

引入CountDownLatch后的执行结果,我们能够控制住main方法的执行,这样能够保证前提任务的执行

2	 上完自习,离开教室
4	 上完自习,离开教室
1	 上完自习,离开教室
5	 上完自习,离开教室
6	 上完自习,离开教室
3	 上完自习,离开教室
main	 班长最后关门

完整代码

import java.util.concurrent.CountDownLatch;

/**
 * @author: wzq
 * @create: 2020-03-15-19:03
 */
public class CountDownLatchDemo {

    public static void main(String[] args) throws InterruptedException {

        // 计数器
        CountDownLatch countDownLatch = new CountDownLatch(6);

        for (int i = 1; i <= 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "\t 上完自习,离开教室");
                countDownLatch.countDown();
            }, String.valueOf(i)).start();
        }

        countDownLatch.await();

        System.out.println(Thread.currentThread().getName() + "\t 班长最后关门");
    }
}

复习枚举

CountDownLatch例子:

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo2 {

    public static void main(String[] args) throws InterruptedException {
        // 计数器
        CountDownLatch countDownLatch = new CountDownLatch(6);

        for (int i = 1; i <= 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "\t 国,被灭");
                countDownLatch.countDown();
            }, CountryEnum.foreach_countryEnum(i).getRetMessage()).start();
        }

        countDownLatch.await();

        System.out.println(Thread.currentThread().getName() + "\t *****秦灭六国,统一华夏");

        System.out.println(CountryEnum.ONE);
        System.out.println(CountryEnum.ONE.getRetCode());
        System.out.println(CountryEnum.ONE.getRetMessage());
    }

}

枚举类:

/**
 * Title:枚举
 * Description:ONE、TWO...相当于名字,其中是属性值,按属性值顺序匹配
 * @author WZQ
 * @version 1.0.0
 * @date 2020/5/7
 */
public enum CountryEnum {

    ONE(1,"齐"),TWO(2,"楚"),THREE(3,"燕"),
    FOUR(4,"赵"),FIVE(5,"魏"),SIX(6,"韩");

    private Integer retCode;

    private String retMessage;

    public Integer getRetCode() {
        return retCode;
    }

    public String getRetMessage() {
        return retMessage;
    }

    CountryEnum(Integer retCode, String retMessage) {
        this.retCode = retCode;
        this.retMessage = retMessage;
    }

    /**
     * 获取对应的值
     * @param index
     * @return
     */
    public static CountryEnum foreach_countryEnum(int index){
        CountryEnum[] countryEnums = CountryEnum.values();

        for (CountryEnum countryEnum : countryEnums) {
            if(index == countryEnum.getRetCode()){
                return countryEnum;
            }
        }
        return null;
    }
}

在这里插入图片描述

CyclicBarrier

概念

和CountDownLatch相反,需要集齐七颗龙珠,召唤神龙。也就是做加法,开始是0,加到某个值的时候就执行

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

案例

集齐7个龙珠,召唤神龙的Demo,我们需要首先创建CyclicBarrier

/**
* 定义一个循环屏障,参数1:需要累加的值,参数2 需要执行的方法
*/
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
	System.out.println("召唤神龙");
});

然后同时编写七个线程,进行龙珠收集,但一个线程收集到了的时候,我们需要让他执行await方法,等待到7个线程全部执行完毕后,我们就执行原来定义好的方法

        for (int i = 0; i < 7; i++) {
            final Integer tempInt = i;
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "\t 收集到 第" + tempInt + "颗龙珠");

                try {
                    // 先到的被阻塞,等全部线程完成后,才能执行方法
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }, String.valueOf(i)).start();
        }

完整代码

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

/**
 * CyclicBarrier循环屏障
 *
 * @author: wzq
 * @create: 2020-03-16-14:40
 */
public class CyclicBarrierDemo {


    public static void main(String[] args) {
        /**
         * 定义一个循环屏障,参数1:需要累加的值,参数2 需要执行的方法
         */
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
            System.out.println("召唤神龙");
        });

        for (int i = 0; i < 7; i++) {
            final Integer tempInt = i;
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "\t 收集到 第" + tempInt + "颗龙珠");

                try {
                    // 先到的被阻塞,等全部线程完成后,才能执行方法
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }, String.valueOf(i)).start();
        }
    }
}

等七个线程运行完才执行System.out.println("召唤神龙");

Semaphore

信号量

概念

信号量主要用于两个目的

  • 一个是用于共享资源的互斥使用
  • 另一个用于并发线程数的控制

代码

我们模拟一个抢车位的场景,假设一共有6个车,3个停车位

那么我们首先需要定义信号量为3,也就是3个停车位

/**
* 初始化一个信号量为3,默认是false 非公平锁, 模拟3个停车位
*/
Semaphore semaphore = new Semaphore(3, false);

然后我们模拟6辆车同时并发抢占停车位,但第一个车辆抢占到停车位后,信号量需要减1

// 代表一辆车,已经占用了该车位
semaphore.acquire(); // 抢占

同时车辆假设需要等待3秒后,释放信号量

// 每个车停3秒
try {
	TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
	e.printStackTrace();
}

最后车辆离开,释放信号量

// 释放停车位
semaphore.release();

完整代码

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * 信号量Demo
 * @author: wzq
 * @create: 2020-03-16-15:01
 */
public class SemaphoreDemo {

    public static void main(String[] args) {

        /**
         * 初始化一个信号量为3,默认是false 非公平锁, 模拟3个停车位
         */
        Semaphore semaphore = new Semaphore(3, false);

        // 模拟6部车
        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                try {
                    // 代表一辆车,已经占用了该车位
                    semaphore.acquire(); // 抢占

                    System.out.println(Thread.currentThread().getName() + "\t 抢到车位");

                    // 每个车停3秒
                    try {
                        TimeUnit.SECONDS.sleep(3);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    
                    System.out.println(Thread.currentThread().getName() + "\t 停3秒离开车位");

                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    // 释放停车位
                    semaphore.release();
                }
            }, String.valueOf(i)).start();
        }
    }
}

运行结果

0	 抢到车位
2	 抢到车位
1	 抢到车位
1	 停3秒离开车位
2	 停3秒离开车位
0	 停3秒离开车位
3	 抢到车位
4	 抢到车位
5	 抢到车位
3	 停3秒离开车位
4	 停3秒离开车位
5	 停3秒离开车位

看运行结果能够发现,0 2 1 车辆首先抢占到了停车位,然后等待3秒后,离开,然后后面 3 4 5 又抢到了车位

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/175728.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

nacos源码分析==客户端从服务端读取配置文件-服务端服务注册

客户端从服务端读取配置文件 客户端启动的时候会扫描到boostrap.yml中的信息&#xff0c;扫描到标签ConditionalOnProperty会将NacosConfigBootstrapConfiguration 中的bean注入。其中NacosConfigProperties就是读取的boostrap.yml中spring.cloud.nacos.config下的配置项。Nac…

“深度学习”学习日记。与学习有关的技巧--权重的初始值

2023.1.22 在深度学习的学习中&#xff0c;权重的初始值特别重要。这关系到神经网络的学习能否成功&#xff1b; 在以前误差反向传播法和神经网络学习的算法实现这两篇文章中&#xff0c;对权重的初始值的确定是这样的&#xff1a; class TwoLayerNet:def __init__(self, inp…

在2022年的最后一天我学会了哈希表

文章目录前言STL相关容器unordered_setunordered_map哈希表哈希冲突闭散列开散列STL相关容器的模拟实现用一个哈希表改造两个容器哈希表的迭代器总结前言 首先先提前祝贺大家新年快乐&#xff01;本文是农历2022年的最后一篇博客。而今天我们介绍的也是STL里面重要的一个数据结…

2023年, 前端路上的开源总结(最新更新...)

19年至今, 笔者利用空余时间陆陆续续做了一些开源项目, 大部分开源项目都是以实际价值为开源基础, 所以我觉得有必要做一个总结和复盘,在复盘的过程中希望也能对大家有所帮助.今后笔者的开源项目都会放在这篇文章中,如果想学习的可以收藏交流.1. 基于react实现的滑动验证码组件…

[LeetCode周赛复盘] 第 96 场双周赛20230121

[LeetCode周赛复盘] 第 96 场双周赛20230121 一、本周周赛总结二、 [Easy] 6300. 最小公共值1. 题目描述2. 思路分析3. 代码实现三、[Medium] 6275. 使数组中所有元素相等的最小操作数 II1. 题目描述2. 思路分析3. 代码实现四、[Medium] 6302. 最大子序列的分数1. 题目描述2. 思…

【JavaScript】33_对象的序列化----JSON

3、对象的序列化 对象的序列化 JS中的对象使用时都是存在于计算机的内存中的 序列化指将对象转换为一个可以存储的格式 在JS中对象的序列化通常是一个对象转换为字符串&#xff08;JSON字符串&#xff09;序列化的用途&#xff08;对象转换为字符串有什么用&#xff09;&…

Linux嵌入式开发——文件系统结构

文章目录Linux嵌入式开发——文件系统结构一、根目录“/”二、Ubuntu文件系统结构三、绝对路径和相对路径Linux嵌入式开发——文件系统结构 一、根目录“/” ​ Linux下“/”就是根目录&#xff01;所有的目录都是由根目录衍生出来的。 二、Ubuntu文件系统结构 /bin 存放二进…

第十届蓝桥杯省赛 C++ A/B组 - 完全二叉树的权值

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 &#x1f4da;专栏地址&#xff1a;蓝桥杯题解集合 &#x1f4dd;原题地址&#xff1a;蜂巢 &#x1f4e3;专栏定位&#xff1a;为想参加蓝桥杯的小伙伴整理常考算法题解&#xff0c;祝大家都能…

Linux C编程一站式学习笔记4

Linux C编程一站式学习笔记 chap4 分支语句 文章目录Linux C编程一站式学习笔记 chap4 分支语句一.if语句语句块习题二.if/else语句引例if/else语句 语法规则if else 的配对原则习题1、写两个表达式&#xff0c;分别取整型变量x的个位和十位2、写一个函数&#xff0c;参数是整型…

常见流对象的使用

文章目录一、缓冲流字节缓冲流字符缓冲流二、转换流字符输入转换流字符输出转换流三、对象序列化对象序列化对象反序列化四、打印流PrintStreamPrintWriter一、缓冲流 缓冲流&#xff1a;也叫高效流或者高级流&#xff0c;我们之前学的字节流称为原始流&#xff0c;缓冲流自带…

【JavaSE】浅析String与StringTable

文章目录1. 前言2. String的两种创建方式2.1 通过new关键字创建一个字符串对象2.2 采用双引号的方式来创建字符串对象2.3 两种方式的区别3. StringTable的位置4. String的intern()方法5. 判断两个字符串是否相等5.1 equals5.2 1. 前言 String类是开发中经常使用的一个类。 对…

第七层:多态

文章目录前情回顾多态多态的基本概念动态多态的满足条件动态多态的使用虚函数多态的优点纯虚函数和抽象类抽象类特点虚析构和纯虚析构虚析构和纯虚析构的共性虚析构和纯虚析构的区别面向对象结束&#xff0c;接下来是什么?本章知识点&#xff08;图片形式&#xff09;&#x1…

数据结构进阶 哈希桶

作者&#xff1a;小萌新 专栏&#xff1a;数据结构进阶 作者简介&#xff1a;大二学生 希望能和大家一起进步&#xff01; 本篇博客简介&#xff1a;模拟实现高阶数据结构 哈希桶 哈希桶哈希冲突的另一种解决方法开散列 -- 链地址法举例哈希表的开散列实现 --哈希桶哈希表的结构…

自动化测试Selenium【基础篇二】

自动化测试Selenium【基础篇二】&#x1f34e;一.Selenium基础使用&#x1f352;1.1 信息打印&#x1f349; 1.1.1打印标题&#x1f349; 1.1.1打印当前网页标题&#x1f352;1.2 窗口&#x1f349;1.2.1 获取句柄&#x1f349;1.2.2 窗口切换&#x1f349;1.2.3 窗口大小设置&…

当你点击浏览器的瞬间都发生了什么----- 网络学习笔记

计算机网络前言web 浏览器协议栈创建套接字阶段。连接阶段。断开阶段。IP模块网卡网络设备 --- 集线器、交换器和路由器集线器交换器路由器路由器的附加功能一 &#xff1a;地址转换路由器的附加功能一 &#xff1a;包过滤功能互联网内部接入网光纤接入网&#xff08;FTTH&…

JDK8 前后的 Date 日期时间 API

JDK8 前后的 Date 日期时间 API 每博一文案 师父说&#xff1a;人只要活在世界上&#xff0c;就会有很多的烦恼&#xff0c;痛苦或是快乐&#xff0c;取决于逆的内心&#xff0c;只要心里拥有温暖灿烂的阳光&#xff0c; 那么悲伤又有什么好畏惧的呢&#xff1f; 人生如行路&a…

vue学习笔记(更新中)

目录 简介 使用Vue写一个"hello&#xff0c;world" 前置准备 代码书写 MVVM模型理解 插值语法和指令语法 插值语法 指令语法 指令&#xff1a;v-bind 指令&#xff1a;v-model vue中的el和data的两种写法 数据代理 方法&#xff1a;defineProperty() 说明…

新年礼物已收到!2022 Apache IoTDB Commits 数量排名 3/364!

社区喜报&#xff01;据 The Apache Software Foundation 官方 Projects Statistics&#xff08;项目信息统计网站&#xff09;的实时数据显示&#xff0c;Apache IoTDB 在过去 12 个月&#xff08;即 2022 年度&#xff09;共发表 6829 Commits&#xff0c;排名 2022 年度 Apa…

2、Three.js开发入门与调试设置

一、添加坐标轴辅助器 AxesHelper 用于简单模拟3个坐标轴的对象. 红色代表 X 轴. 绿色代表 Y 轴. 蓝色代表 Z 轴. 构造函数 AxesHelper( size : Number ) size -- (可选的) 表示代表轴的线段长度. 默认为 1. //添加坐标轴 const axesHelper new THREE.AxesHelper(5); sc…

CSS 特效之心形-彩虹-加载动画

CSS 特效之心形-彩虹-加载动画参考描述效果HTMLCSS重置元素的部分默认样式bodyli动画定义指定animationul抖动代码总汇参考 项目描述搜索引擎BingMDNMDN Web Docs 描述 项目描述Edge109.0.1518.61 (正式版本) (64 位) 效果 HTML <!DOCTYPE html> <html lang"e…