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多线程的使 ...
随机推荐
- jsp表单提交中的逻辑判断
针对于表单 通常情况下 我们都是表单提交 提交的路径为以下: 提交的按钮的type="submit" 当我们想在表单提交前增加一个逻辑判断 我们就需要把button中的typ ...
- Spring Boot入门实验
一. 实验目的和要求 1.掌握使用 IDEA 通过 Maven 和 Spring Initializr 的方式创建 Spring Boot 应用程序: 2.掌握 Maven 的工作原理: 3.了解 s ...
- python 方法调用另一个方法报错,捕获的异常只有message,优化为trackback捕获详细的报错信息
A方法加了try...expect... B方法也加了try....expect... B方法调用了A方法,A方法查找元素,找不到报超时异常,实际B捕获到的异常,只有message\n,没有办法看出是 ...
- spring的问题-能耗、学习曲线
说实话,在过去将近20年中,spring对于it行业的帮助还是很大的,尤其是信息系统建设方面. 但在我看来,spring的发展也许进入了一个困局. 开始的时候,spring的确是一个还是算小巧的工具, ...
- C#winform软件移植上linux的秘密,用GTK开发System.Windows.Forms
国产系统大势所趋,如果你公司的winform界面软件需要在linux上运行,如果软件是用C#开发的,现在我有一个好的快速解决方案. 世界第一的微软的Microsoft Visual Studio,确实 ...
- Python数据分析代码示例
数据清洗 在进行数据分析之前,通常需要对原始数据进行清洗,即处理缺失值.异常值.重复值等问题. 下面是一个数据清洗的示例代码: import pandas as pd # 读取原始数据 data = ...
- vue项目的简单创建与插件下载
准备工作 安装node.js 安装node.js过程全部采用默认配置,一步一步next即可 检验node.js是否安装成功:在cmd命令行中输入node -v以及npm -v 通过cmd创建 安装vu ...
- 3. set 的使用
3. set 的使用 因为在实际工程中,我们不会将具体文件全部写出来,这是一件费力不讨好的事情. 3.1 set 定义变量 在 CMake 文件中,默认的变量数据类型是字符串,如果要用别的类型,需要进 ...
- oeasy教您玩转 linux 010212 管道 pipe
上一部分我们都讲了什么? 牛说cowsay 牛可以有各种表情 可以自定义眼睛 可以变成各种别的小动物 可以说也可以想cowthink 我们也想让牛说出字符画的感觉 回顾字符画 下载figlet和toi ...
- [oeasy]python0024_unix时间戳_epoch_localtime_asctime_PosixTime_unix纪年法
输出时间回忆上次内容 通过搜索 我们学会 import 导入 time 了 完整写法为 asc_time = time.asctime( time.localtime( time.time())) 内 ...