Java中的可重入锁

4

我正在学习Java中的可重入锁。需要对这个概念进行一定的澄清,了解它是如何工作的。以下代码片段是我所理解的:

class Test{
    public synchronized a(){   
       some code;
      b();
       some code;
    }
    public synchronized b(){    
       some code;
    }

}

上面的代码存在可重入锁问题。
我的理解是,假设我们在 Test 共享对象上执行应用程序时有两个线程:T1 和 T2。
无论是 T1 还是 T2,都会同时获取 a() 和 b() 上的锁。假设 T1 首先获取锁并执行 a()。在执行 a() 时,控制流达到 b() 调用。此时,T1 期望为这个 b() 获取新的锁,或者由于它已经持有 b() 的锁,因此跳过锁定。
需要详细解释此行为以及以上代码中的问题。还需要详细解释可重入锁机制如何帮助解决问题,并提供片段和详细解释。
2个回答

6
一个可重入锁允许一个线程在持有锁的情况下再次(多次)获取锁。例如,如果线程T1在对象上调用a()方法,则a()方法会获取该对象上的锁,并开始执行其中的代码。当a()方法的代码调用b()方法时,b()方法将以“可重入”的方式获取相同的锁。当b()方法调用返回时,a()方法仍然持有该锁。只有在a()方法返回时,该锁才会被释放。通常使用引用计数实现可重入锁,以告诉锁实现嵌套深度。参见:https://dev59.com/T2Qn5IYBdhLWcg3w36XV#16504266

2
如果锁不是可重入的,线程在进入a()时获取锁后就无法调用b()
当调用a()并调用b()时,需要使用可重入锁,否则线程将尝试第二次获取锁而导致死锁。相反,它会识别已经拥有锁并增加计数器,以便在b()退出时不释放锁而是减少计数器。

Peter,请您再详细解释一下第一个语句:“如果锁不可重入,线程在持有进入a()时获取的锁的同时将无法调用b()。” - Neeraj
@Neeraj 当锁是非可重入的时候,即使你已经持有它,也无法在某个东西上获取锁。方法也可以是非可重入的,这意味着在该方法内部不能再次调用它。 - Peter Lawrey
好的,我明白了。但是@Peter,我的下一个疑问是,在Java中,通过Synchronized我们获取内在锁。而内在锁默认情况下是可重入的。从您的解释中,我理解到可重入的实用性在于如果需要,它有助于获取相同资源上的同一锁。那么,在Java中ReentrantLock类的实用程序是什么,因为我们已经通过同步机制获取了可重入锁。我的意思是,最好的方法是使用ReentrantLock类还是同步,为什么?请帮忙解答。 - Neeraj
@Neeraj 一个ReentrantLock有额外的功能,这些功能无法通过synchronized实现,例如tryLock和公平锁定。如果您不需要这些额外的功能,我建议您使用普通的synchronized - Peter Lawrey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接