JUC并发常用工具学习
今天主要来和大家分享一下JUC相关的一些简单知识,线程池文章就不介绍了,前面的文章有介绍,本文主要介绍Lock和认识synchronized和并发的一些工具类的使用。
Lock
传统的锁有synchronized关键字,我们可以直接在方法和代码块中使用它。
在Java中有ReentrantLock、ReentrantReadWriteLock

常用的ReentrantLock,默认采用的是非公平锁,也有公平锁的实现方式,简单点说,
公平锁需要根据申请锁的顺序来获取锁,按照先来先服务的原则。
非公平锁可以不按照申请锁的顺序,后申请的线程也可以按先申请锁的线程先获取锁。
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
下面我们也可以通过一个卖票的小例子来看ReentrantLock的使用,其中最主要的是lock和unlock方法,这方法需要成对出现,否则可能会出现锁未释放的情况。
class Ticket1 {
private int number = 50;
Lock lock = new ReentrantLock();
/**
* 卖票的方式
*/
public synchronized void sale() {
// 加锁
lock.lock();
try {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了" + (number--) + "票,剩余" + number);
}
} catch (Exception ex) {
ex.printStackTrace();
} finally {
lock.unlock();
}
}
}
简单对比Lock和synchronized:
synchronized和Lock的类型不同,一个是关键字,一个是类。
Lock可以判断锁的状态,而synchronized是不可以的。
synchronized的锁释放是自动释放的,而Lock是需要手动释放锁。
synchronized线程会一直等待,而Lock是不一定等待下去。
synchronized是可重入锁,是非公平锁,Lock可以重入锁,可以设置是否公平。
Lock适合锁大量代码,synchronized适合锁少量的代码。
生产者的消费者问题
synchronized版
这个问题一直都是比较经典的问题,我们可以使用synchronized关键字实现配置wait方法,notifyAll方法和notify方法实现。
注意wait方法只能出现在同步方法中。
下面的例子需要防止虚假唤醒的问题,即需要采用循环的方式而不能采用if的方式,在JDK1.8文章的也有说明

class Data {
private int number = 0;
/**
* 资源类
*/
public synchronized void increment() {
// if (number != 0) {
while (number != 0) {
// 等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number++;
System.out.println(Thread.currentThread().getName() + "=>" + number);
// 通知其他线程
this.notifyAll();
}
public synchronized void decrement() {
// if (number == 0) {
while (number == 0) {
// 等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number--;
System.out.println(Thread.currentThread().getName() + "=>" + number);
// 通知
this.notifyAll();
}
}
Lock版
可能有人也会说,有synchronize不九可以处理了么,在Java中还有一个配合也是可以实现这个功能的,也就是Lock配置Condition的await方法和signal方法,这个比synchronizd强在哪里呢?这个可以实现精准唤醒,而通过synchronized方法里面的notify不能实现精准唤醒。
下面这个例子就可以实现按顺序实现唤醒。
class Data2 {
private Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
private int number = 1;
public void printA() {
lock.lock();
try {
while (number != 1) {
// 等待
condition1.await();
}
System.out.println(Thread.currentThread().getName() + "=>" + number);
// 唤醒指定的人
number = 2;
condition2.signal();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB() {
lock.lock();
try {
while (number != 2) {
// 等待
condition2.await();
}
System.out.println(Thread.currentThread().getName() + "=>" + number);
// 唤醒指定的人
number = 3;
condition3.signal();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC() {
lock.lock();
try {
while (number != 3) {
// 等待
condition3.await();
}
System.out.println(Thread.currentThread().getName() + "=>" + number);
// 唤醒指定的人
number = 1;
condition1.signal();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
lock.unlock();
}
}
}
理解锁
我们如何判断锁的是谁?锁的是调用该方法的对象还是Class模板呢?这样可以帮助我们深入理解锁。
这里还是采用狂神视频的提到的8锁问题,应该看完就可以理解synchronized锁的判断。
场景一
是输出发短信还是打电话?发短信
发短信延迟4秒,是发短信还是打电话?发短信
synchronized锁的是方法的调用者,两个方法用的是同一把锁,谁先拿到先执行。
public class Test1 {
@SneakyThrows
public static void main(String[] args){
Phone phone = new Phone();
new Thread(() -> {
phone.sendMsg();
}, "A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(() -> {
phone.call();
}, "B").start();
}
}
class Phone {
public synchronized void sendMsg() {
System.out.println("sendMsg");
}
public synchronized void call() {
System.out.println("call");
}
}
场景二
下面先输出哪个结果?发短信还是hello? hello
hello方法是没有锁的,不是同步方法,不受锁影响
public class Test2 {
public static void main(String[] args){
Phone1 phone = new Phone1();
new Thread(() -> {
phone.sendMsg();
}, "A").start();
new Thread(() -> {
phone.call();
}, "B").start();
new Thread(() -> {
phone.hello();
}, "C").start();
}
}
class Phone1 {
public synchronized void sendMsg() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sendMsg");
}
public synchronized void call() {
System.out.println("call");
}
public void hello() {
System.out.println("hello");
}
}
场景三
- 下面重新声明两个对象,一个调用同步发短信方法,一个调用call方法,先执行哪个方法? call
因为两个同步方法,锁的是方法调用者的对象,这里有两把锁
public class Test2 {
public static void main(String[] args){
Phone1 phone1 = new Phone1();
Phone1 phone2 = new Phone1();
new Thread(() -> {
phone1.sendMsg();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.call();
}, "B").start();
}
}
class Phone1 {
public synchronized void sendMsg() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sendMsg");
}
public synchronized void call() {
System.out.println("call");
}
public void hello() {
System.out.println("hello");
}
}
场景四
- 增加两个静态同步方法,只有一个对象,先打印,发短信?打电话?发短信
类一加载就有了,这里锁的Class对象,Class是唯一的
public class Test3 {
public static void main(String[] args){
Phone3 phone3 = new Phone3();
new Thread(() -> {
phone3.sendMsg();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone3.call();
}, "B").start();
}
}
class Phone3 {
public static synchronized void sendMsg() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sendMsg");
}
public static synchronized void call() {
System.out.println("call");
}
public void hello() {
System.out.println("hello");
}
}
两个对象,是发短信还是打电话?发短信
Class模板只有一个
public class Test3 {
public static void main(String[] args){
Phone3 phone3 = new Phone3();
Phone3 phone2 = new Phone3();
new Thread(() -> {
phone3.sendMsg();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.call();
}, "B").start();
}
}
- 一个对象,一个静态同步方法,一个普通同步方法,先发短信,还是先打电话?先打电话
sendMsg锁的是Class模板,call锁的是调用的对象
- 如果是两个对象,结果也是一样,结果还是打电话
public class Test4 {
public static void main(String[] args){
Phone4 phone4 = new Phone4();
new Thread(() -> {
phone4.sendMsg();
}, "A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone4.call();
}, "B").start();
}
}
class Phone4 {
public static synchronized void sendMsg() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sendMsg");
}
public synchronized void call() {
System.out.println("call");
}
}
常见的并发辅助类
CountDownLatch
CountDownLatch里面定义了一个减法计算器,和一个阻塞队列,常用的方法有countDown和await方法,countDown相当于-1,await方法会等待计算器归零,然后再被唤醒向下执行。
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
// 总数6
CountDownLatch countDownLatch = new CountDownLatch(5);
// -1
countDownLatch.countDown();
for (int i = 0; i < 5; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "go out!");
countDownLatch.countDown();
}, String.valueOf(i)).start();
}
// 等待计数器归零,然后再向下执行
countDownLatch.await();
System.out.println("close door!");
}
}
CyclicBarrier
需要等待线程全部达到共同屏障点的同步辅助,其实就是一个加法计算器,主要是可以实现让一组线程达到同一屏障点,然后再进行后续操作。
public class CyclicBarrierDemo {
public static void main(String[] args){
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () ->{
System.out.println("召唤神龙成功!");
});
for (int i = 1; i <= 7; i++) {
int temp = i;
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "收集" + temp + "个龙珠");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
Semaphore
一个计数信号量,其中常用的两个方法acquire用于获取许可证,release释放许可证。
可以用于限流等场景,比如下面的停车场例子,当初始化为一个时,也可以用于做互斥锁。
public class SemaphoreTest {
public static void main(String[] args){
// 停车位
Semaphore semaphore = new Semaphore(3);
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
// 等到
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "抢到车位");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "离开车位");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}, String.valueOf(i)).start();
}
}
}
读写锁
读锁可以允许多个线程同时读,而写锁只允许一个线程写。
下面的例子模拟了一个缓存,这里也采用volatie关键字,用于保证map的数据的可见性。

public class ReadWriteLockDemo {
public static void main(String[] args){
MyCache myCache = new MyCache();
// 多线程写
for (int i = 1; i <= 5; i++) {
final int temp = i;
new Thread(() -> {
myCache.put(temp + "", temp + "");
}, String.valueOf(i)).start();
}
// 多线程读
for (int i = 1; i <= 5; i++) {
final int temp = i;
new Thread(() -> {
myCache.get(temp + "");
}, String.valueOf(i)).start();
}
}
}
class MyCache {
private volatile Map<String, Object> map = new HashMap<>();
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
/**
* 一个线程写
*/
public void put(String key, Object value) {
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "写入");
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入成功!");
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.writeLock().unlock();
}
}
/**
* 多个线程读
*/
public void get(String key) {
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "读取");
Object value = map.get(key);
System.out.println(Thread.currentThread().getName() + "读取成功:" + value);
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
}
}
总结
本文章分享的主要是一些并发工具类的使用和介绍,希望大家喜欢!
参考资料
狂神的JUC视频(B站上有,讲的还可以! )
JUC并发常用工具学习的更多相关文章
- JUC : 并发编程工具类的使用
个人博客网:https://wushaopei.github.io/ (你想要这里多有) 一.JUC是什么 1.JUC定义 JUC,即java.util.concurrent 在并发编程中使用的 ...
- org.apache.commons等常用工具学习
StringUtils 1,StringUtils.isNotBlank isNotEmpty : 判断某字符串是否非空 StringUtils.isNotEmpty(null) = false St ...
- Java并发指南14:JUC中常用的Unsafe和Locksupport
本文转自网络,侵删 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutoria ...
- JUC并发编程学习笔记
JUC并发编程学习笔记 狂神JUC并发编程 总的来说还可以,学到一些新知识,但很多是学过的了,深入的部分不多. 线程与进程 进程:一个程序,程序的集合,比如一个音乐播发器,QQ程序等.一个进程往往包含 ...
- 深度学习框架PyTorch一书的学习-第五章-常用工具模块
https://github.com/chenyuntc/pytorch-book/blob/v1.0/chapter5-常用工具/chapter5.ipynb 希望大家直接到上面的网址去查看代码,下 ...
- 学习游戏服务器开发必看,C++游戏服务器开发常用工具介绍
C++游戏服务器开发常用工具介绍 在软件开发过程中需要使用的工具类型实属众多,从需求建模到软件测试,从代码编译到工程管理,这些工具都对项目有着不可替代的作用.庄子有云,"吾生也有涯,而知也无 ...
- 多线程进阶——JUC并发编程之CountDownLatch源码一探究竟
1.学习切入点 JDK的并发包中提供了几个非常有用的并发工具类. CountDownLatch. CyclicBarrier和 Semaphore工具类提供了一种并发流程控制的手段.本文将介绍Coun ...
- JUC 包下工具类,它的名字叫 LockSupport !你造么?
前言 LockSupport 是 JUC 中常用的一个工具类,主要作用是挂起和唤醒线程.在阅读 JUC 源码中经常看到,所以很有必要了解一下. 公众号:liuzhihangs ,记录工作学习中的技术. ...
- JUC并发编程与高性能内存队列disruptor实战-上
JUC并发实战 Synchonized与Lock 区别 Synchronized是Java的关键字,由JVM层面实现的,Lock是一个接口,有实现类,由JDK实现. Synchronized无法获取锁 ...
- 多线程JUC并发篇常见面试详解
@ 目录 1.JUC 简介 2.线程和进程 3.并非与并行 4.线程的状态 5.wait/sleep的区别 6.Lock 锁(重点) 1.Lock锁 2.公平非公平: 3.ReentrantLock ...
随机推荐
- 二、pycharm的安装
1.python安装教程在上一篇已描述,详情查看: 2.安装pycharm 首先从网站下载pycharm:链接为:http://www.jetbrains.com/pycharm/download/# ...
- 【攻防世界】web练习区write up
目录: view_source robots backup cookie disabled button weak auth simple php xff referer webshell get p ...
- Python项目案例开发从入门到实战-1.2 Python语法基础
书籍信息 1.2 Python语法基础 1.2.1 Python数据类型 数值类型 整型(int):浮点型(float):复数(complex),以j或J结尾,如2+3j 字符串 布尔类型 空值,用N ...
- 个人数据保全计划:(2) NAS基础知识
前言 距离去年国庆入手了NAS至今有好几个月时间了,NAS折腾起来有点麻烦,且实际作用因人而异,并没有想象中的好用,所以说好的这个系列一直没有更新~ 还有另一方面的原因,这些NAS的系统基于Linux ...
- RPA的概念和优势
大多数人每天都会使用到一些机器人流程自动化的工具,例如读取邮件和系统,计算.生成文件和报告.而在未来,那些你不想做的枯燥的工作,也许真的可以不做了,重复化.标准化的工作都可以让机器人帮你完成.想必此刻 ...
- Java日常
学习Java第3天的小总结 一.感受总结 Java是目前市场上的主流语言,相对于C和C++语言来说,竞争更为激烈,而这一大特点也在激励着我不断去探索,去学习: 在下载相应编译环境的过程中,由于IDEA ...
- P4555 [国家集训队]最长双回文串 回文树(回文自动机)简单题
贴个题目链接:https://www.luogu.org/problem/P4555 题目:输入长度为n的串S,求S的最长双回文子串T,即可将T分为两部分X,Y,(∣X∣,∣Y∣≥1)且X和Y都是回文 ...
- Goravel ORM 新增模型关联,用 Golang 写关联也可以跟 Laravel 一样简单
关于 Goravel Goravel 是一个功能完备.具有良好扩展能力的 Web 应用程序框架.作为一个起始脚手架帮助 Golang 开发者快速构建自己的应用.框架风格与 Laravel 保持一致,让 ...
- 2021年9月学科能力综合测试(TACA)试题解答 Mathemaitca练习
各个都是人形计算器???? 目录 试题地址 1 签到 2 3 签到 4 5 6 7 8 9 10 11 你让我猜我肯定这么猜 12 13 试题地址 http://www.mxqe.com/gzsnj/ ...
- singleflight 使用记录以及源码阅读
singleflight 使用方法以及源码阅读 1.简介 安装方式: go get -u golang.org/x/sync/singleflight singleflight 是Go官方扩展同步包的 ...