Java线程中的“Monitor”

10

我在不同的博客中读到了关于显示器的不同观点,所以现在有点困惑。

据我所知,显示器是确保只有一个线程执行临界区代码的人。那么,如果我们有3个同步方法/块,那么我们会有3个显示器来确保只有一个线程进入临界区吗?

如果以上说法是正确的,那么为什么要说在Java中每个对象都有与之关联的显示器?应该是每个同步块都有关联的显示器。


2
是的,但每个同步块都有一个与之关联的监视器(通过synchronized(monitor){...}声明),而该监视器是一个对象。 - Andy Turner
那么你的意思是监视器只是我们正在尝试访问的共享资源? - Deepak Kumar
2
你可以将监视器看作是对象的一部分,它会记住当前正在执行基于该监视器同步的块的线程。它还可以记住线程进入相同监视器(再入锁定)的同步块的次数。监视器还会记住正在等待被通知继续执行代码的其他线程列表。 - Pshemo
3个回答

17

什么是监视器?

监视器是一个线程可以抓取并保持的东西,阻止所有其他线程从抓取相同的监视器并迫使它们等待直到监视器被释放。这就是synchronized块的作用。

这些监视器最初从哪里来?

答案是:任何 Java 对象都可以。例如当你写下:

Object foo = new Object();
synchronized (foo) {
  System.out.println("Hello world.");
}

...这意味着: 当前线程首先会获取与变量foo中存储的对象相关联的监视器,并在打印"Hello world"时保持它,然后释放它。

为什么每个Java对象都有与之关联的监视器?

没有技术上的理由。这是在Java早期版本中做出的设计决策,现在已经太晚改变了(即使在最开始它可能会让人感到困惑并且如果使用不当会引起问题)。


那么在同步方法的情况下会发生什么呢?我们没有与之关联的对象,所以不会使用监视器吗? - Deepak Kumar
1
@DeepakKumar 一个synchronized方法等同于synchronized(this) {},而静态的synchronized方法使用类对象作为其监视器。 - biziclop
谢谢回复。我还有最后一件事,如果我问了太多问题,很抱歉。如果我没记错的话,静态同步方法与类对象无关。那么在这种情况下,我们如何将类对象作为监视器? - Deepak Kumar
2
@DeepakKumar SomeClass.class 也是一个对象(确切地说,它是 Class 类的实例),并且该对象用于同步静态方法。 - Pshemo

7
当使用带有代码块的synchronized时,您需要指定一个对象进行锁定。在这种情况下,该对象的监视器用于锁定。
当使用带有方法的synchronized时,您不需要指定要锁定的对象,而是隐含地使用this对象。同样,this的监视器用于锁定。
因此,对象具有监视器,而同步方法/块没有自己的监视器,而是使用特定对象的监视器。

3
在Java编程上下文中,监视器是Java对象上的内在锁(其中内在意味着“内置”)。对于线程要进入对象上的任何同步实例方法,它必须首先获取该对象上的内在锁。对于线程要进入类上的任何同步静态方法,它必须首先获取该类上的内在锁。
这是Java教程中对监视器的定义:
同步是建立在称为内在锁或监视器锁的内部实体周围的。 (API规范通常仅将此实体称为“监视器”)。
监视器属于对象而不是单个块的一个很好的原因是:监视器在保护对象的状态。对象应该被设计为具有内聚性,这使得实例变量很可能被多个方法引用; 为了保证对象始终处于一致的状态,唯一安全的做法是只允许一个同步方法在该对象上执行。
术语“monitor”来自Concurrent Pascal。请参阅Per Brinch Hansen的论文《Java的不安全并行性》,该论文认为Java实际上没有实现监视器:
Gosling(1996年,第399页)声称Java使用监视器来同步线程。不幸的是,更仔细的检查揭示了Java不支持监视器的概念:
- 除非它们被声明为同步的,否则Java类方法是不同步的。 - 除非它们被声明为私有的,否则Java类变量是公共的(在一个包内)。
同一篇论文中的另一段引述:
未能给出足够的线程交互含义是Java的一个非常严重的缺陷,这破坏了监视器概念的概念完整性。

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