内部锁对 Java 类的实际含义是什么?

为了正确理解Java中并发的问题和解决方案,我正在学习官方Java教程。在其中一个页面中,他们定义了内部锁定和同步链接。在此页面中,他们说:

只要一个线程拥有固有锁,就没有其他线程可以获得相同的锁。另一个线程在尝试获取锁时将阻塞。

此外,他们还在“锁定同步方法”一节中提到:

当线程调用同步方法时,它会自动获取该方法对象的固有锁,并在方法返回时释放它。即使返回是由未捕获的异常引起的,也会发生锁定释放。

对我来说,这意味着一旦我从其中一个线程调用同步方法,我就会持有线程的固有锁,并且

固有锁在同步的两个方面都发挥着作用:强制对对象状态的独占访问,并建立对可见性至关重要的发生前关系。

另一个线程是否无法调用同一类的另一个同步方法?如果是,那么拥有同步方法的整个目的就失败了。不是吗?


答案 1

因此,只需重复我上面的评论作为答案。内部锁定意味着您不必创建对象来同步方法。相比之下,您可以通过调用 来使用外在锁。synchronized(myLock) {...}

这是Java并发实践一书的摘录:“每个对象都有一个内置锁的事实只是一种方便,所以你不需要显式创建锁对象”

书中还说:

对象的固有锁与其状态之间没有固有的关系;对象的字段不需要由其内部锁保护,尽管这是许多类使用的完全有效的锁定约定。获取与对象关联的锁不会阻止其他线程访问该对象获取该对象获取锁的唯一方法就是获取任何其他线程执行的操作。事实上,每个对象都有一个内置的锁,只是为了方便,所以你不需要显式创建锁对象。[9]由您来构建锁定协议或同步策略,以便您安全地访问共享状态,并在整个程序中始终如一地使用它们。

但在脚注中,它说:

[9]回想起来,这个设计决策可能是一个糟糕的决定:它不仅令人困惑,而且迫使JVM实现者在对象大小和锁定性能之间做出权衡。

为了回答你的最后一个问题:你将无法从另一个线程调用同步的方法,但你可以继续从同一线程输入(内部锁是可重入的)。因此,在这种情况下,您必须将锁定想象为来自不同调用方线程的序列化方法访问。

如果您不正确地使用锁定,然后引入了活泼危险,那么是的,它被击败了。这就是为什么你必须确保你的并发线程不会相互争斗得太厉害。

正如Brian Goetz在这段博客文章中所说

那么,在调整应用程序对同步的使用时,我们应该努力减少实际争用量,而不是简单地尝试避免使用同步。


答案 2

似乎你有一个误解(如果它导致了错误的结论,那么dunno),没有人指出。无论如何,一个简短的答案:

固有锁:可以这样想,JVM中的每个对象在内部都有一个锁。 关键字尝试获取目标对象的锁定。每当你,实际发生的事情是synchronizedsynchronized (a) { doSomething; }

  1. 锁定已获取a
  2. 运行同步块中的代码 (doSomething)
  3. 释放锁a

我希望你知道

public synchronized void foo() {
  doSomething;
}

在概念上与

public void foo() {
    synchronized(this) {
        doSomething;
    }
}

好吧,回到你的问题,最大的问题,恕我直言,是:

对我来说,这意味着一旦我从其中一个线程调用同步方法,我就会持有线程的固有锁,并且由于...

是错误的。调用同步方法时,无法掌握线程的锁定。

相反,该线程将拥有“拥有”该方法的对象的固有锁。

例如,在 thread1 中,您调用了 ,并假定它是同步的。thread1 将获取引用的对象的固有锁。a.foo()foo()a

同样,如果调用(并且与静态方法同步),则将获取 Class 对象的固有锁。AClass.bar()barAClass


推荐