可重入锁允许持有锁的线程重复获取同一把锁而不发生阻塞,synchronized和ReentrantLock均实现该特性。JVM通过监视器的持有者线程ID和计数器实现synchronized的可重入,线程首次获取锁时计数器为1,重入时递增,退出同步块时递减,归零后释放锁。ReentrantLock基于AQS框架,通过state变量和持有线程引用实现,支持公平锁、可中断获取、tryLock等高级功能。两者均避免自死锁,适用于递归调用、模块化设计等场景,synchronized更简洁安全,ReentrantLock在高竞争或需细粒度控制时更具优势。

可重入锁允许持有锁的线程在不释放锁的情况下,再次获取该锁。
synchronized
可重入锁的核心在于,它识别当前尝试获取锁的线程是否就是已经持有该锁的线程。如果是同一个线程,它就可以“重新进入”临界区,而不会被阻塞。这种机制避免了线程因为自己持有锁而无法再次获取锁,从而导致死锁的尴尬局面。
具体来说,当一个线程首次获取可重入锁时,锁的计数器会加一。如果同一个线程再次尝试获取该锁,计数器会再次加一,并且锁仍然由该线程持有。只有当计数器归零时,锁才会被完全释放,其他等待的线程才有机会获取它。
synchronized
synchronized(this)
synchronized
举个例子,假设你有一个方法
methodA
methodB
methodB
methodC
methodA
methodB
methodC
public class ReentrantExample {
public synchronized void outerMethod() {
System.out.println(Thread.currentThread().getName() + " 进入 outerMethod");
innerMethod();
System.out.println(Thread.currentThread().getName() + " 退出 outerMethod");
}
public synchronized void innerMethod() {
System.out.println(Thread.currentThread().getName() + " 进入 innerMethod");
// 可以在这里调用更深层次的同步方法
// deepInnerMethod();
System.out.println(Thread.currentThread().getName() + " 退出 innerMethod");
}
public static void main(String[] args) {
ReentrantExample example = new ReentrantExample();
new Thread(() -> example.outerMethod(), "Thread-1").start();
}
}在这个例子中,
Thread-1
outerMethod
example
innerMethod
innerMethod
example
synchronized
可重入锁的实现,无论是
synchronized
java.util.concurrent.locks.ReentrantLock
对于
synchronized
synchronized
synchronized
而对于
ReentrantLock
state
lock()
state
state
state
unlock()
state
state
这种设计使得
ReentrantLock
tryLock()
Condition
synchronized
可重入锁的存在,极大地简化了多线程编程中对共享资源的访问控制,避免了许多潜在的死锁和逻辑复杂性。其核心价值在于它允许“信任”当前持有锁的线程,让其能够自由地调用其他需要相同锁的同步方法或代码块。
最直接的价值体现在避免自死锁(Self-Deadlock)。如果没有可重入性,一个线程在进入一个同步方法后,如果该方法内部又调用了另一个需要相同锁的同步方法,线程就会尝试再次获取一个它自己已经持有的锁,从而导致永久阻塞,形成一个经典的自死锁。可重入锁机制优雅地解决了这个问题,确保了线程在自身操作上的流畅性。
常见的应用场景包括:
递归方法调用:如果一个递归方法本身是同步的,或者在递归过程中需要访问受保护的共享资源,可重入锁是不可或缺的。例如,一个计算阶乘的同步方法,在递归调用自身时,能够顺利地重入,而不会卡死。
public class FactorialCalculator {
private int result;
public synchronized int calculate(int n) {
if (n <= 1) {
result = 1;
return 1;
}
// 递归调用自身,再次获取锁,因为是可重入的,所以不会死锁
int temp = n * calculate(n - 1);
result = temp; // 假设需要同步更新结果
return temp;
}
}封装与模块化设计:在面向对象设计中,一个类可能包含多个方法,它们都操作同一个内部状态,因此都需要对同一个对象进行同步。如果一个外部方法调用了内部方法,而内部方法也需要同步,可重入锁就保证了这种调用链的顺畅。它允许我们更自然地封装和组合同步逻辑,而无需担心因锁的重复获取而导致的阻塞。
框架与库的实现:许多Java并发框架和库在内部实现时,都依赖于可重入锁来保证其内部状态的一致性,同时允许调用者以直观的方式使用这些API,而不用担心底层锁的复杂性。
总的来说,可重入锁使得同步机制更加健壮和易于使用,它允许线程在已经拥有资源访问权限的前提下,继续进行与其相关的操作,这符合我们对“拥有权限”的直观理解。
synchronized
ReentrantLock
synchronized
ReentrantLock
相同点:
不同点:
实现方式与控制粒度:
synchronized
ReentrantLock
java.util.concurrent.locks
lock()
finally
unlock()
功能扩展性:
synchronized
ReentrantLock
tryLock()
tryLock(long timeout, TimeUnit unit)
lockInterruptibly()
new ReentrantLock(true)
Condition
newCondition()
await()
signal()/signalAll()
性能:
synchronized
ReentrantLock
synchronized
ReentrantLock
synchronized
ReentrantLock
选择考量:
synchronized
unlock()
Condition
ReentrantLock
ReentrantLock
finally
synchronized
synchronized
ReentrantLock
总而言之,对于大多数简单的互斥场景,
synchronized
synchronized
ReentrantLock
以上就是什么是可重入锁?为什么synchronized也是可重入的?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号