Java锁

338 阅读3分钟

Java进阶篇--可重入锁 & 不可重入锁

blog.csdn.net/m0_74293254…

deepseek:介绍Java不可重入锁与可重入锁,并比较优缺点

在Java中,锁(Lock)是多线程编程中用于控制对共享资源访问的重要机制。锁可以分为不可重入锁可重入锁,它们在实现和特性上有显著区别。下面详细介绍这两种锁,并比较它们的优缺点。


1. 不可重入锁

不可重入锁是指同一个线程在持有锁的情况下,如果再次尝试获取该锁,会导致线程被阻塞(死锁)。换句话说,不可重入锁不允许同一个线程多次获取同一把锁。

特点:

  • 如果一个线程已经持有锁,再次尝试获取锁时会被阻塞。
  • 实现简单,但容易导致死锁。
  • 需要开发者手动管理锁的获取和释放,避免重复获取。

示例:

public class NonReentrantLock {
    private boolean isLocked = false;

    public synchronized void lock() throws InterruptedException {
        while (isLocked) {
            wait(); // 如果锁已被持有,当前线程等待
        }
        isLocked = true; // 获取锁
    }

    public synchronized void unlock() {
        isLocked = false; // 释放锁
        notify(); // 唤醒等待的线程
    }
}

问题:

如果一个线程在持有锁的情况下,再次调用 lock() 方法,会导致线程永久阻塞(死锁)。


2. 可重入锁

可重入锁是指同一个线程可以多次获取同一把锁,而不会导致死锁。每次获取锁后,必须相应地释放锁,锁的持有计数器会递增和递减。

特点:

  • 允许同一个线程多次获取同一把锁。
  • 避免了因递归调用或嵌套锁导致的死锁问题。
  • 实现相对复杂,但使用更方便。

Java 中的 ReentrantLocksynchronized 关键字都是可重入锁的实现。

示例:

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

public class ReentrantLockExample {
    private final Lock lock = new ReentrantLock();

    public void outer() {
        lock.lock(); // 获取锁
        try {
            System.out.println("Outer method: Lock acquired");
            inner(); // 调用内部方法
        } finally {
            lock.unlock(); // 释放锁
            System.out.println("Outer method: Lock released");
        }
    }

    public void inner() {
        lock.lock(); // 再次获取锁(可重入)
        try {
            System.out.println("Inner method: Lock acquired");
        } finally {
            lock.unlock(); // 释放锁
            System.out.println("Inner method: Lock released");
        }
    }

    public static void main(String[] args) {
        ReentrantLockExample example = new ReentrantLockExample();
        example.outer();
    }
}

优点:

  • 避免死锁:支持递归调用和嵌套锁。
  • 灵活性更高:ReentrantLock 提供了更多功能,如公平锁、超时获取锁、条件变量等。

3. 不可重入锁 vs 可重入锁

特性不可重入锁可重入锁
重入性不支持,同一线程重复获取锁会死锁支持,同一线程可以多次获取锁
实现复杂度简单复杂
死锁风险高(递归调用或嵌套锁会导致死锁)低(支持重入,避免死锁)
灵活性高(如 ReentrantLock 支持超时、公平锁等)
性能较高(实现简单,无额外开销)较低(需要维护锁的持有计数器)
适用场景简单场景,无需嵌套锁复杂场景,如递归、嵌套锁

4. 选择建议

  • 使用不可重入锁

    • 适用于简单的同步场景,且确保不会出现递归调用或嵌套锁。
    • 对性能要求较高,且不需要复杂的锁机制。
  • 使用可重入锁

    • 适用于复杂的多线程场景,如递归调用、嵌套锁。
    • 需要更高的灵活性和功能(如公平锁、超时机制)。

5. 总结

  • 不可重入锁 实现简单,但容易导致死锁,适用于简单场景。
  • 可重入锁 功能强大,支持重入,避免了死锁问题,适用于复杂场景。
  • 在实际开发中,推荐使用可重入锁(如 ReentrantLocksynchronized),因为它们更安全、更灵活。