java多线程之ReentrantLock详解
1.背景
2.基本语法
public class Test01 {
// 定义锁
static ReentrantLock reentrantLock = new ReentrantLock(); public static void main(String[] args) {
// 获取锁
reentrantLock.lock();
try {
// 临界区业务处理
} finally {
// 释放锁
reentrantLock.unlock();
}
}
}
3.特点
1.可重入
2.可打断
3.锁超时
4.公平锁
5.条件变量
3.1.可重入
package com.ldp.demo03ReentrantLock; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.locks.ReentrantLock; /**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 02/02 9:03
* @description <p>
* ReentrantLock特点
* 1.可重入
* 2.可打断
* 3.锁超时
* 4.公平锁
* 5.条件变量
* </p>
* 本节演示可重入特性:
* 可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁
* 如果是不可重入锁,那么第二次获得锁时,自己也会被锁挡住,导致获取锁失败
*/
@Slf4j
public class Test02 {
static ReentrantLock reentrantLock = new ReentrantLock();
/**
* 可重入特性演示
*
* @param args
*/
public static void main(String[] args) {
reentrantLock.lock();
try {
log.info("进入main方法锁");
method01();
} finally {
reentrantLock.unlock();
}
log.info("测试结束");
} public static void method01() {
reentrantLock.lock();
try {
log.info("进入method01方法锁");
method02();
} finally {
reentrantLock.unlock();
}
} public static void method02() {
reentrantLock.lock();
try {
log.info("进入method02方法锁");
} finally {
reentrantLock.unlock();
}
}
}
测试结果:
09:09:18.253 [main] -> 进入main方法锁
09:09:18.253 [main] -> 进入method01方法锁
09:09:18.253 [main] -> 进入method02方法锁
09:09:18.253 [main] -> 测试结束
3.2.可打断
演示代码:
@Slf4j
public class Test03 {
// 定义锁
static ReentrantLock reentrantLock = new ReentrantLock(); /**
* 可打断特性
* @param args
*/
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
// 获取一个可以被打断的锁
reentrantLock.lockInterruptibly();
} catch (InterruptedException e) {
log.info("锁被打断....");
e.printStackTrace();
return;
}
try {
log.info("获得锁....");
} finally {
reentrantLock.unlock();
}
}, "t1");
// 主线程获得锁
log.info("获得了锁");
reentrantLock.lock();
t1.start();
try {
// 等待2秒钟
MyThreadUtil.sleep(2);
t1.interrupt();
} finally {
reentrantLock.unlock();
}
}
}
结果:
10:31:49.841 [main] -> 获得了锁
10:31:51.847 [t1] -> 锁被打断....
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at com.ldp.demo03ReentrantLock.Test03.lambda$main$0(Test03.java:36)
at java.lang.Thread.run(Thread.java:748)
3.3.锁超时
演示代码:
@Slf4j
public class Test04 {
// 定义锁
static ReentrantLock reentrantLock = new ReentrantLock(); /**
* 锁超时
*
* @param args
*/
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
// 获取一个超时锁
boolean tryLock = reentrantLock.tryLock(1, TimeUnit.SECONDS);
if (!tryLock) {
log.info("获取锁超时....");
return;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
log.info("获得锁....");
} finally {
reentrantLock.unlock();
}
}, "t1");
// 主线程获得锁
log.info("获得了锁");
reentrantLock.lock();
t1.start();
try {
// 等待2秒钟
MyThreadUtil.sleep(2);
} finally {
reentrantLock.unlock();
}
}
}
结果:
10:05:47.059 [main] -> 获得了锁
10:05:48.087 [t1] -> 获取锁超时....
3.4.公平锁
// 定义锁,默认是非公平锁(生产上一般使用非公平锁)
static ReentrantLock reentrantLock = new ReentrantLock();
// 定义个【非】公平锁
static ReentrantLock reentrantLock01 = new ReentrantLock(false);
// 定义个公平锁
static ReentrantLock reentrantLock02 = new ReentrantLock(true);
3.5.条件变量
ReentrantLock条件变量
synchronized 中也有条件变量,相当于只有一个条件变量(一个等待空间),即所有的线程都在一个空间等待,要么唤醒空间中的所有,要么唤醒空间中的一个;
ReentrantLock 的条件变量比 synchronized 强大之处在于,它是支持【多个条件变量】(多个等待空间),可以将线程按业务分类,相同业务在同一个等待空间,唤醒时可以唤醒相同类型的业务;
使用要点:
1.await 前需要获得锁
2.await 执行后,会释放锁,进入 conditionObject 等待
3.await 的线程被唤醒(或打断、或超时)取重新竞争 lock 锁,竞争 lock 锁成功后,从 await 后继续执行
案例:
package com.ldp.demo03ReentrantLock; import com.common.MyThreadUtil;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test; import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock; /**
* @author 姿势帝-博客园
* @address https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 02/02 8:53
* @description <p>
* 条件变量
* <p>
* ReentrantLock条件变量
* synchronized 中也有条件变量,相当于只有一个条件变量(一个等待空间),即所有的线程都在一个空间等待,要么唤醒空间中的所有,要么唤醒空间中的一个;
* ReentrantLock 的条件变量比 synchronized 强大之处在于,它是支持【多个条件变量】(多个等待空间),可以将线程按业务分类,相同业务在同一个等待空间,唤醒时可以唤醒相同类型的业务;
* <p>
* 使用要点:
* 1.await 前需要获得锁
* 2.await 执行后,会释放锁,进入 conditionObject 等待
* 3.await 的线程被唤醒(或打断、或超时)取重新竞争 lock 锁,竞争 lock 锁成功后,从 await 后继续执行
* <p>
* 特别提醒:使用await()而不是 wait,否则报错:java.lang.IllegalMonitorStateException
* </p>
* 案例
* 假设家长到学校接孩子放学,不同年级的家长在不同的等待区等待孩子;
* 这里模拟2个年级
* </p>
*/
@Slf4j
public class Test05 {
// 锁
static ReentrantLock reentrantLock = new ReentrantLock();
// 一年级等待区
static Condition firstGradeCondition = reentrantLock.newCondition();
// 二年级等待区
static Condition secondGradeCondition = reentrantLock.newCondition();
// 一年级是否放学
static volatile boolean hasFirst = false;
// 二年级是否放学
static volatile boolean hasSecond = false; /**
* 条件变量
*/
@Test
public void test01() throws InterruptedException {
log.info("测试开始");
Gradle firstGradle = new Gradle("一年级", reentrantLock, firstGradeCondition, hasFirst);
Gradle secondGradle = new Gradle("二年级", reentrantLock, secondGradeCondition, hasSecond);
// 启动线程
firstGradle.start();
secondGradle.start(); // 模拟家长提前到了5秒钟
MyThreadUtil.sleep(5);
// 模拟二年级放学
secondGradle.leaveSchool();
// 2秒后,一年级放学
MyThreadUtil.sleep(1);
firstGradle.leaveSchool(); // 等待线程结束
firstGradle.join();
secondGradle.join();
log.info("测试结束");
} class Gradle extends Thread {
private String name;
private ReentrantLock reentrantLock;
private Condition condition;
private boolean hasGradle; public Gradle(String name, ReentrantLock reentrantLock, Condition condition, boolean hasGradle) {
this.name = name;
this.reentrantLock = reentrantLock;
this.condition = condition;
this.hasGradle = hasGradle;
} @Override
public void run() {
this.setName(name);
reentrantLock.lock();
log.info("获得了锁...");
try {
while (!hasGradle) {
try {
// 没有放学等待
log.info("没有放学,家长在休息室睡觉....");
// condition.wait(); 特别注意这个方法,别错了,wait()是Object的等待方法,会报错:java.lang.IllegalMonitorStateException
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.info("放学了可以带着孩子回家了.....");
} finally {
reentrantLock.unlock();
}
} /**
* 模拟放学
*/
public void leaveSchool() {
reentrantLock.lock();
try {
log.info(name + " 放学了");
// 设置放学标记为true
hasGradle = true;
// 唤醒当前年级所有睡觉的家长
condition.signalAll();
} finally {
reentrantLock.unlock();
}
}
}
}
结果:
11:49:42.176 [main] -> 测试开始
11:49:42.200 [一年级] -> 获得了锁...
11:49:42.200 [一年级] -> 没有放学,家长在休息室睡觉....
11:49:42.201 [二年级] -> 获得了锁...
11:49:42.201 [二年级] -> 没有放学,家长在休息室睡觉....
11:49:47.201 [main] -> 二年级 放学了
11:49:47.201 [二年级] -> 放学了可以带着孩子回家了.....
11:49:48.215 [main] -> 一年级 放学了
11:49:48.215 [一年级] -> 放学了可以带着孩子回家了.....
11:49:48.215 [main] -> 测试结束 Process finished with exit code 0
完美
java多线程之ReentrantLock详解的更多相关文章
- Java多线程之volatile详解
本文目录 从多线程交替打印A和B开始 Java 内存模型中的可见性.原子性和有序性 Volatile原理 volatile的特性 volatile happens-before规则 volatile ...
- Java多线程之ThreadPoolExecutor详解使用
1.概述 我将讲解JAVA原生线程池的基本使用,并由此延伸出JAVA中和线程管理相关的类结构体系,然后我们详细描述JAVA原生线程池的结构和工作方式 2.为什么要使用线程池 前文我们已经讲到,线程是一 ...
- Java多线程之synchronized详解
目录 synchronized简介 同步的原理 对象头与锁的实现 锁的优化与升级 Monitor Record 锁的对比 synchronized简介 synchronized关键字,一般称之为&qu ...
- Java多线程之ReentrantLock与Condition
一.ReentrantLock 1.ReentrantLock简介 ReentrantLock是一个可重入的互斥锁,又被称为“独占锁”.ReentrantLock 类实现了 Lock ,它拥有与 sy ...
- Java多线程之ReentrantLock重入锁简介与使用教程
转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6543947.html 我们知道,线程安全问题需要通过线程之间的同步来解决,而同步大多使用syncrhoize ...
- java多线程之ReentrantLock
前言 相信学过java的人都知道 synchronized 这个关键词,也知道它用于控制多线程对并发资源的安全访问,兴许,你还用过Lock相关的功能,但你可能从来没有想过java中的锁底层的机制是怎么 ...
- iOS多线程之NSOperation详解
使用NSOperation和NSOperationQueue进行多线程开发,只要将一个NSOperation(实际开发中需要使用其子类 NSInvocationOperation,NSBlockOpe ...
- iOS-多线程之NSThread详解
前言 线程是用来执行任务的,线程彻底执行完任务A才能去执行任务B.为了同时执行两个任务,产生了多线程. 我打开一个视频软件,我开辟一个线程A让它执行下载任务,我开辟一个线程B,用来播放视频.我开辟两个 ...
- iOS多线程之GCD详解
GCD(Grand Central Dispatch)是基于C语言开发的一套多线程开发机制.也是目前苹果官方推荐的多线程开发方法.iOS三种多线程开发中GCD是抽象层次最高的.当然用起来也是最简单的. ...
- iOS多线程之NSThread详解
在iOS中每个进程启动后都会建立一个主线程(UI线程),这个线程是其他线程的父线程.由于iOS中除了主线程,其他子线程是独立于Cocoa Touch的,所以只有主线程可以更新UI界面.iOS多线程的使 ...
随机推荐
- jquery中$.get()提交和$.post()提交有区别吗?
相同点: 都是异步请求的方式来获取服务端的数据: 异同点: a.请求方式不同:$.get() 方法使用GET方法来进行异步请求的.$.post() 方法使用POST方法来进行异步请求的. b.参数传递 ...
- 项目管理--PMBOK 读书笔记(3)【项目经理的角色 】
思维导图软件工具:https://www.xmind.cn/ 源文件地址:https://files-cdn.cnblogs.com/files/zj19940610/项目经理的角色.zip
- 给你的博客加上个Live2D看板娘吧
Tips:当你看到这个提示的时候,说明当前的文章是由原emlog博客系统搬迁至此的,文章发布时间已过于久远,编排和内容不一定完整,还请谅解` 给你的博客加上个Live2D看板娘吧 日期:2017-12 ...
- AgileConfig-1.9.4 发布,支持 OpenTelemetry
Hello 大家好,最新版的 AgileConfig 1.9.4 发布了.现在它可以通过 OpenTelemetry 对外提供 logs,traces,metrics 三个维度的数据.用户可以自由选择 ...
- 如何搭建私有的ChatGPT服务
背景 是这样的,我们几个朋友众筹共享一个chatGPT4 Plus账号,且不想多人公用一个账号登录使用web版,想大家各自搞个本地的ChatGPT客户端,共用一个api-key. 我找了一圈,决定使用 ...
- 【vue】利用输入框搜索过滤来选择列表
方法1 <div id="app"> <input type="text" @input="handleInput()" ...
- R语言遍历文件夹求取其中所有栅格文件的平均值
本文介绍基于R语言中的raster包,遍历读取多个文件夹下的多张栅格遥感影像,分别批量对每一个文件夹中的多个栅格图像计算平均值,并将所得各个结果栅格分别加以保存的方法. 其中,本文是用R语言来 ...
- Linux创建新用户时遇到的问题记录
创建新用户命令: useradd -d "/home/guest" -m -s "/bin/bash" guest 报错: useradd: cannot op ...
- 什么是Selenium Grid?如何搭建Selenium Grid?
标签(空格分隔): 测试架构 什么是测试基础架构? 测试基础架构指的是,执行测试的过程中用到的所有基础硬件设施以及相关的软件设施.因此,我们也把测试基础架构称之为广义的测试执行环境.通常来讲,测试基础 ...
- Filter拦截器从入门到快速上手与Listener监听器概述
前置内容: 会话跟踪技术 目录 1. 过滤器Filter 1.1 Filter快速入门 1.2 Filter执行流程 1.3 Filter使用细节 1.4 案例 2. 监听器Listener概述 2. ...