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(); } try { log.info("获得锁...."); } finally { reentrantLock.unlock(); } }, "t1"); // 主线程获得锁 log.info("获得了锁"); reentrantLock.lock(); t1.start(); try { // 等待2秒钟 MyThreadUtil.sleep(2); t1.interrupt(); } finally { reentrantLock.unlock(); } } }
结果:
10:00:51.012 [main] -> 获得了锁 10:00:53.033 [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:32) at java.lang.Thread.run(Thread.java:748) 10:00:53.033 [t1] -> 获得锁.... Exception in thread "t1" java.lang.IllegalMonitorStateException at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151) at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261) at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457) at com.ldp.demo03ReentrantLock.Test03.lambda$main$0(Test03.java:40) 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