目录
当涉及到线程同步和互斥访问共享资源时,可重入锁和不可重入锁是两种常见的锁机制。
可重入锁(ReentrantLock):
- 可重入锁是一种支持重进入的锁机制。重进入是指一个线程在持有锁的情况下,可以再次获取相同的锁而不会被阻塞。
- 可重入锁实现了Lock接口,提供了比内置锁(synchronized关键字)更多的灵活性和功能。
- 可重入锁允许一个线程反复获得该锁,避免了死锁的发生,同时也提高了代码的简洁性和可读性。
- 可重入锁支持公平性设置,使得等待时间最长的线程优先获取锁。
不可重入锁(NonReentrantLock):
- 不可重入锁是一种不支持重进入的锁机制。也就是说,当一个线程获得了不可重入锁之后,如果再次尝试获取锁,就会被阻塞,直到当前持有锁的线程释放锁。
- 不可重入锁在Java中没有内置的实现,需要通过自定义实现或基于AQS(AbstractQueuedSynchronizer)等基础类来构建。
- 不可重入锁可能会导致死锁问题,因为如果一个线程在持有锁的情况下又尝试获取同一个锁,就会导致自己无限等待。
总结
可重入锁允许同一线程多次获得锁,而不可重入锁则不支持同一个线程多次获得锁。在大多数情况下,可重入锁是更常用和推荐的选择,因为它提供了更多的功能、灵活性和安全性,同时避免了死锁问题。但在某些特殊情况下,不可重入锁也可能有其应用场景,例如需要强制确保某段代码只能被一个线程执行。
代码示例
以下是一个将可重入锁(ReentrantLock)和不可重入锁(NonReentrantLock)结合在一起的代码示例,
java
import java.util.concurrent.locks.ReentrantLock;
public class main {
private static ReentrantLock reentrantLock = new ReentrantLock(); // 创建可重入锁对象
private static NonReentrantLock nonReentrantLock = new NonReentrantLock(); // 创建不可重入锁对象
public static void main(String[] args) {
Thread thread1 = new Thread(new ReentrantTask());
Thread thread2 = new Thread(new NonReentrantTask());
thread1.start();
thread2.start();
}
static class ReentrantTask implements Runnable {
@Override
public void run() {
reentrantLock.lock(); // 获取可重入锁
try {
System.out.println(Thread.currentThread().getName() + "进入可重入锁临界区");
criticalSectionWithReentrantLock();
System.out.println(Thread.currentThread().getName() + "离开可重入锁临界区");
} finally {
reentrantLock.unlock(); // 释放可重入锁
}
}
private void criticalSectionWithReentrantLock() {
reentrantLock.lock(); // 可重入锁允许同一线程多次获得锁
try {
// 在可重入锁临界区域执行需要同步的操作
System.out.println(Thread.currentThread().getName() + "正在执行可重入锁临界区操作");
} finally {
reentrantLock.unlock(); // 释放可重入锁
}
}
}
static class NonReentrantTask implements Runnable {
@Override
public void run() {
nonReentrantLock.lock(); // 获取不可重入锁
try {
System.out.println(Thread.currentThread().getName() + "进入不可重入锁临界区");
criticalSectionWithNonReentrantLock();
System.out.println(Thread.currentThread().getName() + "离开不可重入锁临界区");
} finally {
nonReentrantLock.unlock(); // 释放不可重入锁
}
}
private void criticalSectionWithNonReentrantLock() {
nonReentrantLock.lock(); // 不可重入锁不允许同一线程多次获得锁
try {
// 在不可重入锁临界区域执行需要同步的操作
System.out.println(Thread.currentThread().getName() + "正在执行不可重入锁临界区操作");
} finally {
nonReentrantLock.unlock(); // 释放不可重入锁
}
}
}
static class NonReentrantLock {
private boolean isLocked = false;
public synchronized void lock() {
while (isLocked) {
try {
wait(); // 等待直到当前线程获取锁
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
isLocked = true;
}
public synchronized void unlock() {
isLocked = false;
notify(); // 唤醒等待的线程
}
}
}
在这个示例中,我们创建了一个可重入锁对象 reentrantLock 和一个不可重入锁对象 nonReentrantLock ,并在 ReentrantTask 和 NonReentrantTask 类中分别使用这两种类型的锁来实现线程同步。
ReentrantTask 类使用可重入锁,在 run() 方法中获取可重入锁并进入临界区域执行操作,而且可以在临界区域内再次获得相同的锁。这展示了可重入锁的特性。
NonReentrantTask 类使用不可重入锁,在 run() 方法中获取不可重入锁并进入临界区域执行操作,但是如果尝试再次获取相同的锁,将被阻塞。这展示了不可重入锁的特性。
注意,不可重入锁 NonReentrantLock 是通过自定义类实现的。它使用一个布尔变量 isLocked 来表示当前锁是否被占用,并在 lock() 和 unlock() 方法中使用 synchronized 关键字和 wait()、notify() 方法实现线程同步。
请注意,混合使用可重入锁和不可重入锁可能需要谨慎处理,以避免死锁和其它线程同步问题。