Java多线程打辅助的三个小伙子
前言
之前学多线程的时候没有学习线程的同步工具类(辅助类)。ps:当时觉得暂时用不上,认为是挺高深的知识点就没去管了..
在前几天,朋友发了一篇比较好的Semaphore文章过来,然后在浏览博客的时候又发现面试还会考,那还是挺重要的知识点。于是花了点时间去了解一下。
Java为我们提供了三个同步工具类:
- CountDownLatch(闭锁)
- CyclicBarrier(栅栏)
- Semaphore(信号量)
这几个工具类其实说白了就是为了能够更好控制线程之间的通讯问题~
一、CountDownLatch
1.1CountDownLatch简介
- A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.
简单来说:CountDownLatch是一个同步的辅助类,允许一个或多个线程一直等待,直到其它线程完成它们的操作。
它常用的API其实就两个:await()
和countDown()
使用说明:
- count初始化CountDownLatch,然后需要等待的线程调用await方法。await方法会一直受阻塞直到count=0。而其它线程完成自己的操作后,调用
countDown()
使计数器count减1。当count减到0时,所有在等待的线程均会被释放 - 说白了就是通过count变量来控制等待,如果count值为0了(其他线程的任务都完成了),那就可以继续执行。
1.2CountDownLatch例子
例子:3y现在去做实习生了,其他的员工还没下班,3y不好意思先走,等其他的员工都走光了,3y再走。
import java.util.concurrent.CountDownLatch;
public class Test {
public static void main(String[] args) {
final CountDownLatch countDownLatch = new CountDownLatch(5);
System.out.println("现在6点下班了.....");
// 3y线程启动
new Thread(new Runnable() {
@Override
public void run() {
try {
// 这里调用的是await()不是wait()
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("...其他的5个员工走光了,3y终于可以走了");
}
}).start();
// 其他员工线程启动
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("员工xxxx下班了");
countDownLatch.countDown();
}
}).start();
}
}
}
输出结果:
再写个例子:3y现在负责仓库模块功能,但是能力太差了,写得很慢,别的员工都需要等3y写好了才能继续往下写。
import java.util.concurrent.CountDownLatch;
public class Test {
public static void main(String[] args) {
final CountDownLatch countDownLatch = new CountDownLatch(1);
// 3y线程启动
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("3y终于写完了");
countDownLatch.countDown();
}
}).start();
// 其他员工线程启动
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("其他员工需要等待3y");
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("3y终于写完了,其他员工可以开始了!");
}
}).start();
}
}
}
输出结果:
参考资料:
- https://blog.csdn.net/qq_19431333/article/details/68940987
- https://blog.csdn.net/panweiwei1994/article/details/78826072
- http://www.importnew.com/15731.html
二、CyclicBarrier
2.1CyclicBarrier简介
- A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point. CyclicBarriers are useful in programs involving a fixed sized party of threads that must occasionally wait for each other. The barrier is called cyclic because it can be re-used after the waiting threads are released.
简单来说:CyclicBarrier允许一组线程互相等待,直到到达某个公共屏障点。叫做cyclic是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用(对比于CountDownLatch是不能重用的)
使用说明:
- CountDownLatch注重的是等待其他线程完成,CyclicBarrier注重的是:当线程到达某个状态后,暂停下来等待其他线程,所有线程均到达以后,继续执行。
2.2CyclicBarrier例子
例子:3y和女朋友约了去广州夜上海吃东西,由于3y和3y女朋友住的地方不同,自然去的路径也就不一样了。于是他俩约定在体育西路地铁站集合,约定等到相互见面的时候就发一条朋友圈。
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class Test {
public static void main(String[] args) {
final CyclicBarrier CyclicBarrier = new CyclicBarrier(2);
for (int i = 0; i < 2; i++) {
new Thread(() -> {
String name = Thread.currentThread().getName();
if (name.equals("Thread-0")) {
name = "3y";
} else {
name = "女朋友";
}
System.out.println(name + "到了体育西");
try {
// 两个人都要到体育西才能发朋友圈
CyclicBarrier.await();
// 他俩到达了体育西,看见了对方发了一条朋友圈:
System.out.println("跟" + name + "去夜上海吃东西~");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
测试结果:
玩了一天以后,各自回到家里,3y和女朋友约定各自洗澡完之后再聊天
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class Test {
public static void main(String[] args) {
final CyclicBarrier CyclicBarrier = new CyclicBarrier(2);
for (int i = 0; i < 2; i++) {
new Thread(() -> {
String name = Thread.currentThread().getName();
if (name.equals("Thread-0")) {
name = "3y";
} else {
name = "女朋友";
}
System.out.println(name + "到了体育西");
try {
// 两个人都要到体育西才能发朋友圈
CyclicBarrier.await();
// 他俩到达了体育西,看见了对方发了一条朋友圈:
System.out.println("跟" + name + "去夜上海吃东西~");
// 回家
CyclicBarrier.await();
System.out.println(name + "洗澡");
// 洗澡完之后一起聊天
CyclicBarrier.await();
System.out.println("一起聊天");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
测试结果:
参考资料:
三、Semaphore
3.1Semaphore简介
Semaphores are often used to restrict the number of threads than can access some (physical or logical) resource.
- A counting semaphore. Conceptually, a semaphore maintains a set of permits. Each {@link #acquire} blocks if necessary until a permit is available, and then takes it. Each {@link #release} adds a permit,potentially releasing a blocking acquirer.However, no actual permit objects are used; the {@code Semaphore} just
keeps a count of the number available and acts accordingly.
Semaphore(信号量)实际上就是可以控制同时访问的线程个数,它维护了一组"许可证"。
- 当调用
acquire()
方法时,会消费一个许可证。如果没有许可证了,会阻塞起来 - 当调用
release()
方法时,会添加一个许可证。 - 这些"许可证"的个数其实就是一个count变量罢了~
3.2Semaphore例子
3y女朋友开了一间卖酸奶的小店,小店一次只能容纳5个顾客挑选购买,超过5个就需要排队啦~~~
import java.util.concurrent.Semaphore;
public class Test {
public static void main(String[] args) {
// 假设有50个同时来到酸奶店门口
int nums = 50;
// 酸奶店只能容纳10个人同时挑选酸奶
Semaphore semaphore = new Semaphore(10);
for (int i = 0; i < nums; i++) {
int finalI = i;
new Thread(() -> {
try {
// 有"号"的才能进酸奶店挑选购买
semaphore.acquire();
System.out.println("顾客" + finalI + "在挑选商品,购买...");
// 假设挑选了xx长时间,购买了
Thread.sleep(1000);
// 归还一个许可,后边的就可以进来购买了
System.out.println("顾客" + finalI + "购买完毕了...");
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}
输出结果:
反正每次只能5个客户同时进酸奶小店购买挑选。
参考资料:
- https://blog.csdn.net/qq_19431333/article/details/70212663
- https://blog.csdn.net/panweiwei1994/article/details/78827248
四、总结
Java为我们提供了三个同步工具类:
- CountDownLatch(闭锁)
- 某个线程等待其他线程执行完毕后,它才执行(其他线程等待某个线程执行完毕后,它才执行)
- CyclicBarrier(栅栏)
- 一组线程互相等待至某个状态,这组线程再同时执行。
- Semaphore(信号量)
- 控制一组线程同时执行。
本文简单的介绍了一下这三个同步工具类是干嘛用的,要深入还得看源码或者借鉴其他的资料。
最后补充一下之前的思维导图知识点:
参考资料:
- 《Java并发编程实战》
- http://www.cnblogs.com/dolphin0520/p/3920397.html
- https://zhuanlan.zhihu.com/p/27829595
如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y。为了大家方便,刚新建了一下qq群:742919422,大家也可以去交流交流。谢谢支持了!希望能多介绍给其他有需要的朋友
文章的目录导航:
Java多线程打辅助的三个小伙子的更多相关文章
- Java 多线程:锁(三)
Java 多线程:锁(三) 作者:Grey 原文地址: 博客园:Java 多线程:锁(三) CSDN:Java 多线程:锁(三) StampedLock StampedLock其实是对读写锁的一种改进 ...
- java多线程学习笔记(三)
java多线程下的对象及变量的并发访问 上一节讲到,并发访问的时候,因为是多线程,变量如果不加锁的话,会出现“脏读”的现象,这个时候需要“临界区”的出现去解决多线程的安全的并发访问.(这个“脏读”的现 ...
- Java 多线程详解(三)------线程的同步
Java 多线程详解(一)------概念的引入:http://www.cnblogs.com/ysocean/p/6882988.html Java 多线程详解(二)------如何创建进程和线程: ...
- java多线程编程核心技术——第三章
第一节等待/通知机制 1.1不使用等待/通知机制实现线程间的通讯 1.2什么是等待/通知机制 1.3等待/通知机制的实现 1.4方法wait()锁释放与notify()锁不释放 1.5当interru ...
- java多线程编程核心技术——第三章总结
第一节等待/通知机制 1.1不使用等待/通知机制实现线程间的通讯 1.2什么是等待/通知机制 1.3等待/通知机制的实现 1.4方法wait()锁释放与notify()锁不释放 1.5当interru ...
- Java多线程基础知识(三)
一. 管道输入/输出流 它和其它文件输入/输出流或网络输入/输出流的不同之处,它主要是线程之间的数据传输,而传输的媒介是内存. 管道输入/输出流主要包含四中实现: 1. PipedOutputStre ...
- java多线程基本概述(三)——同步方法
非线程安全其实是在多个线程对同一个对象实例的变量进行并发访问的时候发生,产生的后果就是脏读,也就是取到的数据是修改过的.而线程安全就是获得的实例变量的值是经过同步处理的,从而不会出现脏读现象. 1.1 ...
- java多线程快速入门(三)
通过实现Runnable接口实现多线程 package com.cppdy; //通过实现Runnable接口实现多线程 class MyThread1 implements Runnable{ @O ...
- Java多线程学习笔记(三)同步和异步
首先是一段代码: public class HasSelfPrivateNum { public void addI(String username){ try { int num=0; if(use ...
随机推荐
- bzoj 3759 Hungergame 博弈论+线性基
和nim游戏类似 易证必败状态为:当前打开的箱子中石子异或和为0,没打开的箱子中不存在一个子集满足异或和为0 因为先手无论是取石子还是开箱子,后手都可以通过取石子来使状态变回原状态 所以只需判定是否有 ...
- codeForces 472D 最小生成树
题目大意:给出一个图中点的两两距离,问是否是一棵树,若是,求出平均边权最大的点 prim最小生成树,若原图是树,则最小生成树的距离就是原距离.否则不是. 搞出来树了,第二问随便dfs就好了. #inc ...
- BZOJ_1433_[ZJOI2009]假期的宿舍_二分图匹配
BZOJ_1433_[ZJOI2009]假期的宿舍_二分图匹配 题意: 学校放假了······有些同学回家了,而有些同学则有以前的好朋友来探访,那么住宿就是一个问题.比如A 和B都是学校的学生,A要回 ...
- MYSQL—— 完整性约束条件中primary key、auto_increment使用总结!
完整性约束条件主要有:primary key(主键), auto_increment(自增长), poreign key(外键), not null(非空), unique key(唯一), defa ...
- MNIST手写识别
Demo侠可能是我等小白进阶的必经之路了,如今在AI领域,我也是个研究Demo的小白.用了两三天装好环境,跑通Demo,自学Python语法,进而研究这个Demo.当然过程中查了很多资料,充分发挥了小 ...
- 使用Entity Framework Core访问数据库(DB2篇)
前言 上一篇讲了一些EF Core访问Oracle的坑.(感兴趣请移步:使用Entity Framework Core访问数据库(Oracle篇)) 这篇主要讲一下关于EF Core访问DB2的一揽子 ...
- 哎呀,我老大写Bug啦——记一次MessageQueue的优化
MessageQueue,顾名思义消息队列,在系统开发中也是用的比较多的一个中间件吧.我们这里主要用它来做日志管理和订单管理的,记得老老大(恩,是的,就是老老大,因为他已经跳槽了)还在的时候,当时也是 ...
- 页面性能优化-原生JS实现图片懒加载
在项目开发中,我们往往会遇到一个页面需要加载很多图片的情况.我们可以一次性加载全部的图片,但是考虑到用户有可能只浏览部分图片.所以我们需要对图片加载进行优化,只加载浏览器窗口内的图片,当用户滚动时,再 ...
- java游戏开发杂谈 - 线程
线程,让游戏拥有了动态变化的能力. java的图形界面,在启动的时候,就开始了一个线程. 这个线程负责处理:JFrame.JPanel等的绘制.事件处理. 它是由操作系统调用的,在程序启动时开启,程序 ...
- Redis的正确使用姿势
前言 说到分布式缓存,可能大多数人脑海浮现的就是redis了,为什么redis能够在竞争激烈的缓存大战中脱颖而出呢?原因无非有一下几点:性能好,丰富的特性跟数据结构,api操作简单.但是用的人多了,就 ...