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 

* ReentrantLock特点 * 1.可重入 * 2.可打断 * 3.锁超时 * 4.公平锁 * 5.条件变量 *

* 本节演示可重入特性: * 可重入是指同一个线程如果首次获得了这把锁,那么因为它是这把锁的拥有者,因此有权利再次获取这把锁 * 如果是不可重入锁,那么第二次获得锁时,自己也会被锁挡住,导致获取锁失败
*/ @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 

* 条件变量 *

* ReentrantLock条件变量 * synchronized 中也有条件变量,相当于只有一个条件变量(一个等待空间),即所有的线程都在一个空间等待,要么唤醒空间中的所有,要么唤醒空间中的一个; * ReentrantLock 的条件变量比 synchronized 强大之处在于,它是支持【多个条件变量】(多个等待空间),可以将线程按业务分类,相同业务在同一个等待空间,唤醒时可以唤醒相同类型的业务; *

* 使用要点: * 1.await 前需要获得锁 * 2.await 执行后,会释放锁,进入 conditionObject 等待 * 3.await 的线程被唤醒(或打断、或超时)取重新竞争 lock 锁,竞争 lock 锁成功后,从 await 后继续执行 *

* 特别提醒:使用await()而不是 wait,否则报错:java.lang.IllegalMonitorStateException *

* 案例 * 假设家长到学校接孩子放学,不同年级的家长在不同的等待区等待孩子; * 这里模拟2个年级 *

*/ @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

完美