Java 可重入锁 详解

Java 可重入锁 (ReentrantLock) 详解

在 Java 中,可重入锁 (ReentrantLock)java.util.concurrent.locks 包中的一个类,它实现了 Lock 接口。可重入锁为多线程环境中的线程同步提供了更灵活的锁机制,通常用于替代 synchronized 关键字。


1. 什么是可重入锁?

可重入锁(ReentrantLock) 是一种允许线程在持有锁的情况下多次获取同一个锁的机制。换句话说,如果一个线程已经持有了锁,并在同一个线程中再次请求该锁,则该线程将被允许继续持有锁,而不会被阻塞。这种行为称为 锁的重入性

示例解释:
import java.util.concurrent.locks.ReentrantLock;

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

    public void methodA() {
        lock.lock();  // 获取锁
        try {
            System.out.println("Method A is executing.");
            methodB();  // 在持有锁的情况下调用另一个方法
        } finally {
            lock.unlock();  // 释放锁
        }
    }

    public void methodB() {
        lock.lock();  // 再次获取同一个锁
        try {
            System.out.println("Method B is executing.");
        } finally {
            lock.unlock();  // 释放锁
        }
    }

    public static void main(String[] args) {
        ReentrantLockExample example = new ReentrantLockExample();
        example.methodA();
    }
}
输出:
Method A is executing.
Method B is executing.

在上面的例子中,methodAmethodB 都使用了同一个锁对象。由于 methodA 在持有锁时调用了 methodB,但由于 ReentrantLock 支持重入,methodB 可以正常执行。


2. 可重入锁的核心特性

特性描述
重入性 (Reentrancy)同一线程可以多次获取同一把锁,而不会被阻塞。
公平锁与非公平锁可重入锁支持公平和非公平锁(默认是非公平锁)。
锁中断支持支持线程在等待锁时的中断响应。
条件变量支持 (Condition)支持多个条件变量,可以更灵活地实现等待/通知机制。

3. 可重入锁的主要方法

方法描述
lock()获取锁,如果锁不可用,则等待。
lockInterruptibly()获取锁,支持中断等待过程。
tryLock()尝试获取锁,成功返回 true,失败返回 false
tryLock(long time, TimeUnit unit)尝试在指定时间内获取锁,超时失败。
unlock()释放锁。
isHeldByCurrentThread()检查当前线程是否持有该锁。
getHoldCount()返回当前线程持有该锁的次数(重入次数)。
isLocked()检查锁是否被任意线程持有。

4. 可重入锁的公平性和非公平性

  • 非公平锁(默认)

    • 在争夺锁时,线程的调度不遵循严格的先来先服务规则。
    • 性能更高,适用于高吞吐量场景。
    • 默认构造方法:
      ReentrantLock lock = new ReentrantLock();  // 非公平锁
      
  • 公平锁(需要手动设置)

    • 锁的获取遵循先来先服务(FIFO)规则。
    • 开销更大,性能稍低。
    • 设置公平锁:
      ReentrantLock lock = new ReentrantLock(true);  // 公平锁
      

5. 可重入锁的应用示例

示例 1:使用 tryLock() 避免死锁
import java.util.concurrent.locks.ReentrantLock;

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

    public void tryLockExample() {
        if (lock.tryLock()) {
            try {
                System.out.println("Lock acquired, executing critical section.");
            } finally {
                lock.unlock();
            }
        } else {
            System.out.println("Failed to acquire lock.");
        }
    }

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

示例 2:实现生产者-消费者模型
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

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

    public static void main(String[] args) {
        Thread producer = new Thread(() -> {
            while (true) {
                lock.lock();
                try {
                    while (count >= 10) {
                        notFull.await();  // 阻塞,直到 notFull 条件满足
                    }
                    count++;
                    System.out.println("Produced: " + count);
                    notEmpty.signal();  // 通知消费者可以消费
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        });

        Thread consumer = new Thread(() -> {
            while (true) {
                lock.lock();
                try {
                    while (count <= 0) {
                        notEmpty.await();  // 阻塞,直到 notEmpty 条件满足
                    }
                    System.out.println("Consumed: " + count);
                    count--;
                    notFull.signal();  // 通知生产者可以生产
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        });

        producer.start();
        consumer.start();
    }
}

6. 使用注意事项

  1. 手动释放锁:使用 lock() 时,需要在 finally 块中调用 unlock(),以确保锁能够正常释放,避免死锁。
  2. 公平与性能权衡:公平锁适用于需要严格遵循 FIFO 的场景,但性能较低,非公平锁性能更高。
  3. 尽量缩小锁的范围:将锁的作用域尽量缩小,以减少锁的持有时间,提高程序性能。
  4. 条件变量的使用Condition 对象应与 ReentrantLock 配合使用,以实现更加灵活的线程间通信。

总结

  • ReentrantLock(可重入锁) 是 Java 提供的一个强大的线程同步机制。
  • 它提供了比 synchronized 关键字更灵活的功能,如公平锁、可中断锁、尝试获取锁等。
  • 在多线程编程中,了解如何正确使用 ReentrantLock 是编写高性能和可靠并发程序的关键。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

飞滕人生TYF

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值