如果一个线程试图获取它已经拥有的锁,会发生什么?

时间:2016-07-30 20:41:30

标签: java multithreading concurrency

这取自Joshua Bloch撰写的一本书。

我不是英语母语人士,因此有理由要求澄清疑问。

  

因为内部锁是可重入的,所以如果线程尝试   获取它已经拥有的锁,请求   成功。可重入意味着在a上获取锁   每线程而不是**每次调用。

基于每次调用,他是否意味着在每个方法调用? 考虑一下代码段:

class Factoriser{

        public synchronized void doSomething(){
           // code goes here
        }
}

假设有一个线程A,并且能够获取具有实例方法doSomething()的对象的锁定。由于某种原因,相同的线程线程A再次获取对同一对象实例方法doSomething()的锁定(想象一下,之前的锁还没有被释放)。

如果我正确理解了约书亚的陈述,那么即使有2个方法调用/调用,也只会有一个锁。我的理解是100%正确的。请举例说明。我很困惑,因为作者在下面的段落中澄清了这一点,这进一步困扰了我。

  

通过将每个锁与获取计数和拥有线程相关联来实现可重入性。当计数为零时,锁定被视为未被保留。当线程获取以前未保留的锁时,JVM会记录所有者   并将采集计数设置为1。如果那个线程相同   再次获取锁定,计数递增,并且   当拥有的线程退出同步块时,   计数递减。当计数达到零时,锁定被释放。

如果Reentrancy / locks获取不是基于每次调用,为什么JVM为我上面描述的场景设置的计数设置为2?

1 个答案:

答案 0 :(得分:2)

计数器用于匹配锁定加料和粪便。仅当计数器为0时才能释放锁定。将方法foo()标记为synchronized并在对象obj上调用它与以下块相同:

// calling obj.foo()
synchronized(obj) {
    // Do the foo-work
}

假设我们有两个同步方法:foo()bar(),后者从前者调用。调用将具有以下结构:

final FooBar obj = new FooBar();

// calling obj.foo()
synchronized(obj) { // 1. here the lock is acquired, the counter is set to 1

    // do some foo-work

    // calling obj.bar()
    synchronized(obj) {  // 2. the same lock object, the counter is set to 2
        // do the bar-work
    } // 3. the counter is set to 1, the lock is still not released

    // continue doing the foo-work
} // 4. the counter is 0, the lock is released

如果没有计数器,在步骤3我们将释放锁定,这将是一个错误,因为我们仍在外部同步块中。因此,需要计数器才能正确实现重入。