Java中的“monitor”是什么?

159
在Java并发编程中,"monitor"是指一个锁机制。当我读到“每个对象都有一个相关联的监视器”时,这意味着什么?它是一个特殊的对象吗?
6个回答

111

监视器是一种控制对象并发访问的机制。

这样可以实现以下功能:

线程 1:

public void a()
{
    synchronized(someObject) {
        // do something (1)
    }
}

线程2:

public void b()
{
    synchronized(someObject) {
        // do something else (2)
    }
}

这可以防止线程1和2同时访问被监视(同步)的部分。其中一个将启动,监视器将防止另一个在第一个完成之前访问该区域。

它不是一个特殊的对象,而是放置在类层次结构根处的同步机制:java.lang.Object

还有waitnotify方法,也将使用对象的监视器在不同的线程之间进行通信。


1
我们可以这样说,当我们创建一个同步方法时,我们正在为该对象的方法定义一个锁(监视器)吗? - xdevel2000
没错。当你有一个同步方法(我在我的答案中没有提到)时,你正在创建该方法的监视器。 - Pablo Santa Cruz
35
不完全正确。每个对象自动关联一个监视器(互斥锁),与其他任何因素无关。当您声明一个方法为 synchronized 时,您在声明运行时必须在方法开始执行之前获取对象的监视器锁定(并且在控制返回到调用代码之前必须释放锁定)。 - Andrzej Doyle
41
@Pablo - 方法没有独立的监视器;监视器只存在于对象中,大多数方法的封闭实例或静态方法的相应Class对象将成为它们的监视器。如果您已经有一个同步的method1()并且您声明method2()为同步的,则不会创建新的监视器,并且实际上调用任一方法(在相同的对象上)将尝试锁定相同的监视器。这往往会让新手措手不及。 - Andrzej Doyle
@Andrzej:我明白了...好的。我会在答案中修复它。谢谢! - Pablo Santa Cruz
1
@Andrzej:所以,每个对象都有一个关联的监视器。然后我可以有许多同步方法。每当线程调用其中任何一个方法时,它都会获取该监视器来执行同步操作。 - xdevel2000

33

监视器是一个实体,它拥有等待集合。在Java中,任何Object都可以充当监视器。

关于Java中监视器的工作原理的详细说明,请阅读《Java并发编程实战》中的监视器机制部分(上述链接显示了Google图书的预览,该部分可供阅读)。


1
正如您所说,“监视器是一个实体..”,这是否意味着监视器是具有/跟踪锁定和等待集的内部对象/状态?如果不是,请在此处详细说明实体?基本上,当我们的Java文档关于notifyall()说“唤醒所有正在等待此对象监视器的线程。”时,我得到的是对象正在维护(借助内部实体/对象)所有正在等待锁定的线程,该内部实体/对象称为监视器? - user3198603
最有用的答案。感谢@JRL。 - gravetii
对我来说,它显示为:“此页面无法预览。” - opncow

18

在并发编程中,我们需要关注两件事:

  1. 互斥

当一个进程/线程正在执行其临界区时,其他进程/线程不允许执行其临界区。 (每个进程都有一个名为“临界区”的代码段,在其中访问共享数据。)

  1. 同步

当线程通过协作共同实现共同目标时,这些线程需要彼此协作。 当它们专注于共同目标时,它们需要进行同步。

监视器用于实现互斥和同步。


如何轻松理解监视器?

高级别的监视器视图

不要混淆此临界区与临界区,因为此处所述的临界区属于对象级别,而不是线程级别。 共享数据被视为临界区。

每个对象及其类都与监视器相关联。需要受到并发访问保护的对象的实例变量包括与该对象相关联的监视器的临界区,需要受到并发访问保护的类/类的静态变量包括与该类相关联的监视器的临界区。

  • 此临界区受锁的保护,此锁确保互斥。

  • 等待集也与监视器相关联,用于在线程之间提供协调。

  • 一个入口集用于保存已经请求锁但尚未获得锁的线程。

如何在监视器上实现互斥?


每个对象都与监视器相关联,该监视器具有一个,当它访问共享变量时,每个线程都可以使用此锁定或解锁对象。明确来说,这意味着每次只有一个线程可以持有监视器上的。尝试锁定该的任何其他线程都会被阻塞,直到它们可以获得该。当新线程尝试获取锁时,如果已经有一个线程拥有锁,则该线程将在入口集上等待以获取锁。当获得锁的线程完成其临界区时,它将释放锁。因此,下一个线程将获取锁,但此下一个线程将从入口集中取出,并由JVM根据一些标准(如FIFO)确定。

在这里,我们实现了互斥,因为我们向线程提供对对象的独占访问权,并且我们不允许任何其他线程进入其临界区。

使用监视器实现互斥的示例Java代码

  class Counter
      {
            private int count = 0;
            public void synchronized Increment() {
                int n = count;
                count = n+1;
            } //Here synchronized is used to indicate those things should be done sequentially.
      }


如何通过监视器实现协调/同步?

使用与监视器相关的等待集(wait set)以及“等待和通知(wait and notify)”或“信号和继续(signal and continue)”机制可实现同步。在某个线程需要特定状态下的数据,而另一个线程负责将数据置于该状态时(例如生产者/消费者问题),同步非常重要。

当线程调用wait()方法并传递对象时,线程被挂起并添加到等待集中,直到其他线程在同一对象上调用notify()或notifyAll()。

notify()方法用于唤醒处于特定对象的等待集中的线程。有两种通知等待线程的方式:

  • notify() --> 对于等待集中所有线程,方法notify()会通知其中任意一个线程进行任意唤醒。唤醒哪个线程取决于JVM,是非确定性的。
  • notifyAll() --> 此方法简单地唤醒等待在等待集中的所有线程。唤醒的线程不能继续执行,直到当前线程释放此对象上的锁。唤醒的线程将像任何可能正在主动竞争同步的其他线程一样进行竞争。

以下是使用监视器在生产者消费者问题中实现同步的示例Java代码

class Buffer {
            private char [] buffer;
            private int count = 0, in = 0, out = 0;

            Buffer(int size)
            {
                 buffer = new char[size];
            }
 
            public synchronized void Put(char c) {
                 while(count == buffer.length) 
                 {
                      try { wait(); }
                      catch (InterruptedException e) { } 
                      finally { } 
                 } 
                 System.out.println("Producing " + c + " ...");
                 buffer[in] = c; 
                 in = (in + 1) % buffer.length; 
                 count++; 
                 notify(); 
            }
    
            public synchronized char Get() {
                 while (count == 0) 
                 {
                      try { wait(); }
                      catch (InterruptedException e) { } 
                      finally { } 
                 } 
                 char c = buffer[out]; 
                 out = (out + 1) % buffer.length;
                 count--;
                 System.out.println("Consuming " + c + " ..."); 
                 notify(); 
                 return c;
            }
      }
参考以下链接: http://www.csc.villanova.edu/~mdamian/threads/javamonitors.html#:~:text=Java%20associates%20a%20monitor%20with,the%20monitor%20for%20that%20object https://howtodoinjava.com/java/multi-threading/how-to-use-locks-in-java-java-util-concurrent-locks-lock-tutorial-and-example/

15
  1. 监视器是一个概念/机制,不仅限于Java语言;
  2. “在并发编程中,监视器是一个旨在由多个线程安全使用的对象或模块”;
  3. 正如每个读者所知道的那样,在Java中,每个对象都是java.lang.Object的子类。java开发人员使java.lang.Object具有功能和特征,使Java程序员可以将任何对象用作监视器。例如,每个对象都有等待队列、重入队列和等待和通知方法,使其成为监视器;
  4. 这里阅读关于监视器的更多信息。


0
监视器与对象或数据成员相关联,当一个数据成员或对象进入同步块(临界区)时获取,并在退出时释放。

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