Java:多线程计数

本笔记是根据bilibili上 尚硅谷 的课程 Java大厂面试题第二季 而做的笔记

1. CountDownLatch

概念

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

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

场景

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

解决方案

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

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

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

for (int i = 0; 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	 上完自习,离开教室
0 上完自习,离开教室
main 班长最后关门
2 上完自习,离开教室
3 上完自习,离开教室
4 上完自习,离开教室
5 上完自习,离开教室
6 上完自习,离开教室

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

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

完整代码

import java.util.concurrent.CountDownLatch;

public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException { // 计数器
CountDownLatch countDownLatch = new CountDownLatch(6); for (int i = 0; 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 班长最后关门");
}
}

2. 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();
}

完整代码

/**
* CyclicBarrier循环屏障
*/
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();
}
}
}

运行结果:

1	 收集到 第1颗龙珠
0 收集到 第0颗龙珠
2 收集到 第2颗龙珠
3 收集到 第3颗龙珠
4 收集到 第4颗龙珠
5 收集到 第5颗龙珠
6 收集到 第6颗龙珠
召唤神龙

3. 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();

完整代码

/**
* 信号量Demo
*/
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 离开车位");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 释放停车位
semaphore.release();
}
}, String.valueOf(i)).start();
}
}
}

运行结果:

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

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

Java:多线程计数的更多相关文章

  1. Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock

    本章对ReentrantLock包进行基本介绍,这一章主要对ReentrantLock进行概括性的介绍,内容包括:ReentrantLock介绍ReentrantLock函数列表ReentrantLo ...

  2. Java多线程系列--“JUC锁”01之 框架

    本章,我们介绍锁的架构:后面的章节将会对它们逐个进行分析介绍.目录如下:01. Java多线程系列--“JUC锁”01之 框架02. Java多线程系列--“JUC锁”02之 互斥锁Reentrant ...

  3. java多线程系类:JUC线程池:03之线程池原理(二)(转)

    概要 在前面一章"Java多线程系列--"JUC线程池"02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包 ...

  4. java多线程系类:JUC锁:01之框架

    本章,我们介绍锁的架构:后面的章节将会对它们逐个进行分析介绍.目录如下:01. Java多线程系列--"JUC锁"01之 框架02. Java多线程系列--"JUC锁&q ...

  5. Java多线程系列--“JUC锁”09之 CountDownLatch原理和示例

    概要 前面对"独占锁"和"共享锁"有了个大致的了解:本章,我们对CountDownLatch进行学习.和ReadWriteLock.ReadLock一样,Cou ...

  6. Java多线程系列--“JUC锁”06之 Condition条件

    概要 前面对JUC包中的锁的原理进行了介绍,本章会JUC中对与锁经常配合使用的Condition进行介绍,内容包括:Condition介绍Condition函数列表Condition示例转载请注明出处 ...

  7. Java多线程系列--“JUC锁”08之 共享锁和ReentrantReadWriteLock

    概要 Java的JUC(java.util.concurrent)包中的锁包括"独占锁"和"共享锁".在“Java多线程系列--“JUC锁”02之 互斥锁Ree ...

  8. Java多线程系列--“JUC锁”11之 Semaphore信号量的原理和示例

    概要 本章,我们对JUC包中的信号量Semaphore进行学习.内容包括:Semaphore简介Semaphore数据结构Semaphore源码分析(基于JDK1.7.0_40)Semaphore示例 ...

  9. Java多线程系列--“JUC集合”04之 ConcurrentHashMap

    概要 本章是JUC系列的ConcurrentHashMap篇.内容包括:ConcurrentHashMap介绍ConcurrentHashMap原理和数据结构ConcurrentHashMap函数列表 ...

随机推荐

  1. fetch ios低版本兼容cannot clone a disturbed response

    报错信息 ios 11以下 cannot clone a disturbed response github.com/github/fetc- 问题发生场景 使用了一个或者多个三方库 三方库或者自己的 ...

  2. Windows下安装程序时提示未安装Microsoft Net FrameWork 2.0

    问题描述 安装程序时碰到如下: 现在基本都是用win7.win10系统,缺少环境大多数都是因为系统没有启用. 解决方法 控制面板 - 程序 - 启用或关闭Windows功能 - 把第一项'NET Fr ...

  3. 6步快速配置Tomcat环境变量(Win10)

    一.配置 tomcat环境变量之前先安装jdk和配置jdk的环境变量 1.首先右击我的电脑(此电脑),点击属性,或者也可以从控制面板上打开,如下图,找到系统点击高级系统设置: 2.然后进入系统属性界面 ...

  4. 解决vscode可以编译通过c++项目,但头文件有红色波浪线的问题

    解决vscode可以编译通过c++项目,但头文件有红色波浪线的问题 一.问题描述 我是在Ubuntu 16.04的环境下,用vscode写代码的,一般不使用vscode自带的编译环境,而是用cmake ...

  5. 求 10000 以内 n 的阶乘

    求 10000以内 n 的阶乘. 输入格式 只有一行输入,整数 n(0≤n≤10000) 输出格式 一行,即 n!的值. 输出时每行末尾的多余空格,不影响答案正确性 样例输入 100 样例输出 933 ...

  6. HCNP Routing&Switching之路由控制、路由策略和IP-Prefix List

    前文我们了解了IS-IS路由聚合和认证相关话题,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/15306645.html:今天我们来聊一聊路由控制技术中的路由策 ...

  7. SpringAOP-动态代理,日志注入

    SpringAOP 前言: 1.AOP定义? 用来干啥的? 怎么用?(怎么跑通它的思路) 代理模式 为啥要学代理模式? -- 因为是SpringAop的底层 原有的代码不敢动,一动就是Bug,.所以使 ...

  8. 使用ImageMagick操作gif图

    上篇文章我们已经学习了 GraphicsMagick 中的许多函数,也说过 GraphicsMagick 是 ImageMagick 的一个分支,所以他们很多的函数都是一样的使用方式和效果,相似的内容 ...

  9. 目标检测之pycocotools安装

    从清华镜像源下载https://pypi.tuna.tsinghua.edu.cn/simple/pycocotools-windows/ wheel型包,pycocotools_windows-2. ...

  10. MNIST手写数字识别:卷积神经网络

    代码 import torch from torchvision import datasets from torch.utils.data import DataLoader import torc ...