ReentrantLock 介绍-Java

ReentrantLock 是 Java 并发包(java.util.concurrent.locks)提供的一种显式锁机制。相比于 synchronizedReentrantLock 提供了更多的锁控制功能,例如可中断锁、定时锁、条件变量和公平锁。

1. ReentrantLock

ReentrantLock 是一种 可重入的显式锁。它的可重入性允许同一个线程多次获取同一把锁(无需陷入死锁),并通过手动调用 lock()unlock() 实现锁的获取和释放。

  • 显式锁:需要手动获取(lock())和释放(unlock())。
  • 可重入性:同一线程可以多次获取同一把锁,每次获取都需显式释放。

2. ReentrantLock 的基本用法

以下是 ReentrantLock 的基本用法:

  1. 创建锁对象:
  2. 获取锁:
  3. 释放锁:

示例代码:基本用法

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample {
    private final ReentrantLock lock = new ReentrantLock();
    private int counter = 0;

    public void increment() {
        lock.lock(); // 获取锁
        try {
            counter++;
        } finally {
            lock.unlock(); // 确保释放锁
        }
    }

    public int getCounter() {
        lock.lock();
        try {
            return counter;
        } finally {
            lock.unlock();
        }
    }
}

3. ReentrantLock 的高级功能

ReentrantLock 提供了以下增强功能,相较于 synchronized 更加灵活。

(1) 可中断锁

线程在尝试获取锁时可以被中断。这是通过 lockInterruptibly() 方法实现的。

lock.lockInterruptibly(); // 可中断的获取锁

使用示例

public void safeMethod() {
    try {
        lock.lockInterruptibly(); // 如果被中断,将抛出 InterruptedException
        // 关键逻辑
    } catch (InterruptedException e) {
        System.out.println("Thread was interrupted while waiting for lock");
    } finally {
        lock.unlock();
    }
}

(2) 锁超时

支持尝试在指定时间内获取锁,如果超时仍未获取到锁,则返回 false

if (lock.tryLock(2, TimeUnit.SECONDS)) {
    try {
        // 获取锁后执行逻辑
    } finally {
        lock.unlock();
    }
} else {
    System.out.println("Failed to acquire lock within 2 seconds");
}

(3) 公平锁与非公平锁

  • 非公平锁(默认):线程获取锁的顺序不一定按照请求锁的顺序,性能更高。
  • 公平锁:线程获取锁时按照请求锁的先后顺序,保证公平性。

创建公平锁

ReentrantLock lock = new ReentrantLock(true); // 公平锁

(4) 条件变量

ReentrantLock 提供了 Condition 对象,用于线程之间的协调。一个锁可以有多个条件变量,通过条件变量可以实现更灵活的线程同步。

常用方法

  • await():使线程等待,并释放锁。
  • signal():唤醒一个等待线程。
  • signalAll():唤醒所有等待线程。

示例:生产者-消费者模型

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class ProducerConsumer {
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition notEmpty = lock.newCondition();
    private final Condition notFull = lock.newCondition();

    private final int[] buffer = new int[10];
    private int count = 0, putIndex = 0, takeIndex = 0;

    public void produce(int value) throws InterruptedException {
        lock.lock();
        try {
            while (count == buffer.length) {
                notFull.await(); // 等待缓冲区有空位
            }
            buffer[putIndex] = value;
            putIndex = (putIndex + 1) % buffer.length;
            count++;
            notEmpty.signal(); // 唤醒消费者
        } finally {
            lock.unlock();
        }
    }

    public int consume() throws InterruptedException {
        lock.lock();
        try {
            while (count == 0) {
                notEmpty.await(); // 等待缓冲区有数据
            }
            int value = buffer[takeIndex];
            takeIndex = (takeIndex + 1) % buffer.length;
            count--;
            notFull.signal(); // 唤醒生产者
            return value;
        } finally {
            lock.unlock();
        }
    }
}

4. ReentrantLock 的实现原理

(1) 基于 AQS 实现

ReentrantLock 的核心是基于 AbstractQueuedSynchronizer (AQS) 实现的,它维护了一个 FIFO 的线程等待队列。

  • 独占模式ReentrantLock 的锁是一种独占锁,只有一个线程可以持有。
  • 可重入性ReentrantLock 记录当前线程获取锁的次数(通过 state 变量),每次释放锁时 state 减少,直到为 0 时完全释放。

(2) 加锁与解锁

  • lock() 方法通过 CAS 操作尝试将状态从 0 修改为 1
  • 如果锁已经被占用,线程会加入 AQS 的等待队列。
  • 释放锁时,unlock() 会将状态从 1 修改为 0,并唤醒等待队列中的线程。

5. ReentrantLock 的优缺点

优点

  1. 灵活性更高:支持可中断锁。支持超时获取锁。支持条件变量,灵活实现线程协调。
  2. 公平锁:提供公平锁选项,保证线程按请求顺序获取锁。
  3. 更适合高并发场景:性能更高,适用于复杂并发需求。

缺点

  1. 复杂性增加:必须显式调用 lock() 和 unlock(),使用不当可能导致死锁。
  2. 代码冗长:相较于 synchronized,代码更加冗长。

6. 使用建议

使用 ReentrantLock 的场景

  • 需要使用 超时锁中断锁
  • 需要实现复杂的线程同步逻辑(如条件变量)。
  • 需要公平锁机制确保线程按顺序执行。
  • 需要更高性能或灵活性时。

使用 synchronized 的场景

  • 同步逻辑简单,代码清晰是首要需求。
  • 不需要复杂的线程协调功能。
Java碎碎念 文章被收录于专栏

来一杯咖啡,聊聊Java的碎碎念呀

全部评论

相关推荐

评论
2
收藏
分享

创作者周榜

更多
正在热议
更多
# 春招至今,你的战绩如何? #
11975次浏览 106人参与
# 你的实习产出是真实的还是包装的? #
2079次浏览 43人参与
# MiniMax求职进展汇总 #
24306次浏览 310人参与
# 军工所铁饭碗 vs 互联网高薪资,你会选谁 #
7744次浏览 43人参与
# 简历第一个项目做什么 #
31835次浏览 345人参与
# 重来一次,我还会选择这个专业吗 #
433667次浏览 3926人参与
# 米连集团26产品管培生项目 #
6286次浏览 216人参与
# 当下环境,你会继续卷互联网,还是看其他行业机会 #
187312次浏览 1122人参与
# 牛客AI文生图 #
21469次浏览 238人参与
# 不考虑薪资和职业,你最想做什么工作呢? #
152560次浏览 888人参与
# 研究所笔面经互助 #
118993次浏览 577人参与
# 简历中的项目经历要怎么写? #
310551次浏览 4230人参与
# AI时代,哪些岗位最容易被淘汰 #
64089次浏览 838人参与
# 面试紧张时你会有什么表现? #
30531次浏览 188人参与
# 你今年的平均薪资是多少? #
213254次浏览 1039人参与
# 你怎么看待AI面试 #
180338次浏览 1266人参与
# 高学历就一定能找到好工作吗? #
64352次浏览 620人参与
# 你最满意的offer薪资是哪家公司? #
76650次浏览 374人参与
# 我的求职精神状态 #
448238次浏览 3129人参与
# 正在春招的你,也参与了去年秋招吗? #
363732次浏览 2638人参与
# 腾讯音乐求职进展汇总 #
160726次浏览 1112人参与
# 校招笔试 #
471657次浏览 2964人参与
牛客网
牛客网在线编程
牛客网题解
牛客企业服务