内部锁实际上对Java类意味着什么?

Swa*_*nil 9 java concurrency multithreading synchronization

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

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

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

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

对我来说,这意味着一旦我从其中一个线程调用同步方法,我将拥有该线程的内在锁定,因为

内部锁在同步的两个方面都起作用:强制对对象状态进行独占访问,并建立对可见性至关重要的先发生关系.

另一个线程是否无法调用同一个类的另一个同步方法?如果是,则使同步方法的整个目的失败.不是吗?

Cap*_*tti 8

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

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

书中还说:

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

但在脚注中它说:

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

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

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

正如Brian Goetz在此博客文章中所说:

在调整应用程序的同步使用时,我们应该尽量减少实际争用的数量,而不是简单地试图避免使用同步


Adr*_*hum 6

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

内在锁:只要认为它,JVM中的每个对象都内部有一个锁. synchronizedkeywords尝试获取目标对象的锁.无论何时synchronized (a) { doSomething; },实际发生的事情都是

  1. 锁在a被获取
  2. synchronized块中的代码是run(doSomething)
  3. 释放锁定 a

我希望你知道

public synchronized void foo() {
  doSomething;
}
Run Code Online (Sandbox Code Playgroud)

在概念上是相同的

public void foo() {
    synchronized(this) {
        doSomething;
    }
}
Run Code Online (Sandbox Code Playgroud)

好的,回到你的问题,最大的问题,imho,是:

对我来说,这意味着,一旦我打电话从一个线程同步的方法,我将有内在锁持有线程和自...

这是错的.当您调用同步方法时,您无法获得线程的锁定.

相反,该线程将拥有"拥有"该方法的对象的内部锁.

例如,在thread1中,您调用a.foo(),并假设foo()已同步.thread1将获取a引用对象的内部锁.

类似地,如果AClass.bar()调用(并且bar是同步和静态方法),AClass将获取Class对象的内部锁.


eri*_*son 5

一个锁一次只能被一个线程持有。这并没有违背目的;这就是目的。

线程MUT ually通过获取锁,或CLUDE彼此在临界区同时作用互斥。这为一系列不同的操作提供了有效的原子性,因此其他线程永远不会看到可能违反一致性保证的中间状态。