跳转到内容

Java ReentrantLoc

来自代码酷


ReentrantLock 是 Java 并发包(`java.util.concurrent.locks`)中提供的一种可重入互斥锁,它提供了比传统 `synchronized` 关键字更灵活、更强大的线程同步机制。与 `synchronized` 不同,`ReentrantLock` 允许显式地获取和释放锁,并支持公平锁策略、可中断锁等待以及条件变量等高级功能。

基本概念[编辑 | 编辑源代码]

什么是可重入锁?[编辑 | 编辑源代码]

可重入锁(Reentrant Lock)是指同一个线程可以多次获取同一把锁而不会导致死锁。例如,如果一个线程已经持有了锁,它可以再次请求该锁而不会被阻塞。`ReentrantLock` 实现了这一特性,因此被称为“可重入锁”。

ReentrantLock 的核心特性[编辑 | 编辑源代码]

  • 可重入性:线程可以重复获取已持有的锁。
  • 公平性:支持公平锁(先请求锁的线程先获得锁)和非公平锁(默认)。
  • 可中断性:线程在等待锁的过程中可以响应中断。
  • 超时机制:支持尝试获取锁的超时机制。
  • 条件变量:可以通过 `Condition` 实现更精细的线程等待/通知机制。

基本用法[编辑 | 编辑源代码]

创建 ReentrantLock[编辑 | 编辑源代码]

`ReentrantLock` 的构造函数可以指定是否为公平锁:

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample {
    private final ReentrantLock lock = new ReentrantLock(); // 非公平锁(默认)
    private final ReentrantLock fairLock = new ReentrantLock(true); // 公平锁
}

获取和释放锁[编辑 | 编辑源代码]

使用 `lock()` 方法获取锁,`unlock()` 方法释放锁。通常需要在 `try-finally` 块中释放锁以确保异常时锁仍能被释放:

public void performTask() {
    lock.lock(); // 获取锁
    try {
        // 临界区代码
        System.out.println("Lock acquired by " + Thread.currentThread().getName());
    } finally {
        lock.unlock(); // 释放锁
        System.out.println("Lock released by " + Thread.currentThread().getName());
    }
}

可重入性示例[编辑 | 编辑源代码]

以下代码展示了 `ReentrantLock` 的可重入特性:

public void recursiveMethod(int depth) {
    lock.lock();
    try {
        System.out.println("Depth: " + depth + ", Lock held by: " + Thread.currentThread().getName());
        if (depth > 0) {
            recursiveMethod(depth - 1); // 递归调用,仍能获取锁
        }
    } finally {
        lock.unlock();
    }
}

高级功能[编辑 | 编辑源代码]

公平锁与非公平锁[编辑 | 编辑源代码]

  • 非公平锁(默认):不保证线程获取锁的顺序,可能导致某些线程“插队”。
  • 公平锁:按照线程请求锁的顺序分配锁,避免饥饿现象,但可能降低吞吐量。

示例:

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

尝试获取锁[编辑 | 编辑源代码]

`tryLock()` 方法尝试获取锁,如果锁不可用则立即返回 `false`,避免线程阻塞:

if (lock.tryLock()) {
    try {
        // 成功获取锁
    } finally {
        lock.unlock();
    }
} else {
    // 锁不可用
}

支持超时版本的 `tryLock`:

if (lock.tryLock(1, TimeUnit.SECONDS)) { // 等待最多 1 秒
    try {
        // 成功获取锁
    } finally {
        lock.unlock();
    }
} else {
    // 超时未获取锁
}

可中断锁[编辑 | 编辑源代码]

`lockInterruptibly()` 允许线程在等待锁的过程中响应中断:

try {
    lock.lockInterruptibly(); // 可中断获取锁
    try {
        // 临界区代码
    } finally {
        lock.unlock();
    }
} catch (InterruptedException e) {
    // 处理中断
}

条件变量(Condition)[编辑 | 编辑源代码]

`Condition` 提供了类似 `Object.wait()` 和 `Object.notify()` 的功能,但更灵活:

private final Condition notEmpty = lock.newCondition();
private final Condition notFull = lock.newCondition();

public void put(Object item) throws InterruptedException {
    lock.lock();
    try {
        while (queue.isFull()) {
            notFull.await(); // 等待队列非满
        }
        queue.add(item);
        notEmpty.signal(); // 通知队列非空
    } finally {
        lock.unlock();
    }
}

实际应用案例[编辑 | 编辑源代码]

线程安全的计数器[编辑 | 编辑源代码]

以下是一个使用 `ReentrantLock` 实现的线程安全计数器:

public class Counter {
    private int count = 0;
    private final ReentrantLock lock = new ReentrantLock();

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    public int getCount() {
        lock.lock();
        try {
            return count;
        } finally {
            lock.unlock();
        }
    }
}

生产者-消费者模型[编辑 | 编辑源代码]

使用 `ReentrantLock` 和 `Condition` 实现生产者-消费者模型:

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

public class ProducerConsumer {
    private final Queue<Integer> queue = new LinkedList<>();
    private final int capacity = 5;
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();

    public void produce(int item) throws InterruptedException {
        lock.lock();
        try {
            while (queue.size() == capacity) {
                notFull.await(); // 等待队列非满
            }
            queue.add(item);
            notEmpty.signal(); // 通知消费者
        } finally {
            lock.unlock();
        }
    }

    public int consume() throws InterruptedException {
        lock.lock();
        try {
            while (queue.isEmpty()) {
                notEmpty.await(); // 等待队列非空
            }
            int item = queue.remove();
            notFull.signal(); // 通知生产者
            return item;
        } finally {
            lock.unlock();
        }
    }
}

性能与最佳实践[编辑 | 编辑源代码]

  • 在简单场景下,`synchronized` 的性能可能与 `ReentrantLock` 相当,但 `ReentrantLock` 在复杂场景下更灵活。
  • 始终在 `finally` 块中释放锁,避免锁泄漏。
  • 避免嵌套锁(多个锁的交叉持有),以减少死锁风险。
  • 公平锁会降低吞吐量,仅在必要时使用。

与 synchronized 的对比[编辑 | 编辑源代码]

特性 ReentrantLock synchronized
可重入性 支持 支持
公平性 支持(可选) 不支持
可中断性 支持 不支持
超时机制 支持 不支持
条件变量 支持(`Condition`) 有限(`wait/notify`)
锁释放 必须显式调用 `unlock()` 自动释放

总结[编辑 | 编辑源代码]

`ReentrantLock` 是 Java 并发编程中的重要工具,提供了比 `synchronized` 更丰富的功能。它适用于需要高级锁特性的场景,如公平锁、可中断锁或条件变量。然而,对于简单的同步需求,`synchronized` 仍然是更简洁的选择。开发者应根据具体需求选择合适的同步机制。

模板:Java Concurrency