Java并发编程中的管程(Monitor)模型

字数 1192阅读 20

简述

A Monitor defines a lock and zero or more condition variables for managing concurrent access to shared data.

管程(Monitor)是一种和信号量(Sophomore)等价的同步机制。它在Java并发编程中也非常重要,虽然程序员没有直接接触管程,但它确实是synchronizedwait()/notify()等线程同步和线程间协作工具的基石:当我们在使用这些工具时,其实是它在背后提供了支持。简单来说:

  • 管程使用锁(lock)确保了在任何情况下管程中只有一个活跃的线程,即确保线程互斥访问临界区

  • 管程使用条件变量(Condition Variable)提供的等待队列(Waiting Set)实现线程间协作,当线程暂时不能获得所需资源时,进入队列等待,当线程可以获得所需资源时,从等待队列中唤醒

Java语言规范中提及的Monitor

在Java语言规范中,虽然没有具体定义Monitor该如何实现,但至少说明了wait()/notify()synchronized等关键词和它密切相关。

The Java programming language provides multiple mechanisms for communicating between threads. The most basic of these methods is synchronization, which is implemented using monitors. Each object in Java is associated with a monitor, which a thread can lock or unlock. Only one thread at a time may hold a lock on a monitor. Any other threads attempting to lock that monitor are blocked until they can obtain a lock on that monitor. A thread t may lock a particular monitor multiple times; each unlock reverses the effect of one lock operation.

Java编程语言提供了多种线程之间通信的机制。 这些方法中最基本的是同步,该同步是通过monitor实现的。Java中的每个对象都与一个monitor关联,线程可以lockunlock monitor。 一次只能有一个线程在monitor上持有锁。 尝试lock该monitor的所有其他线程将被阻止,直到它们可以在该monitor上获得锁为止。 线程t可以多次lock特定的monitor。 每次unlock都会逆转一次lock操作的效果。

The synchronized statement (§14.19) computes a reference to an object; it then attempts to perform a lock action on that object's monitor and does not proceed further until the lock action has successfully completed. After the lock action has been performed, the body of the synchronized statement is executed. If execution of the body is ever completed, either normally or abruptly, an unlock action is automatically performed on that same monitor.

synchronized语句(§14.19)计算对对象的引用; 然后,它尝试在该对象的监视器上执行锁定操作,并且在锁定操作成功完成之前不会进一步进行操作。 执行锁定动作后,将执行synchronized语句的主体。 如果主体的执行正常或突然完成,则会在同一监视器上自动执行解锁动作。

除此之外,Java语言规范里也定义了wait()/notify()/notifyAll()的语义,以及它们和等待集这一底层实现的关系。

Every object, in addition to having an associated monitor, has an associated wait set. A wait set is a set of threads.

每个对象除具有关联的monitor外,还具有关联的等待集。等待集是一个线程集合。

When an object is first created, its wait set is empty. Elementary actions that add threads to and remove threads from wait sets are atomic. Wait sets are manipulated solely through the methods Object.wait, Object.notify, and Object.notifyAll.

首次创建对象时,其等待集为空。将线程添加到等待集中或从等待集中删除线程的基本操作是原子的。等待集仅通过Object.wait, Object.notifyObject.notifyAll方法操作。

HotSpot虚拟机中的管程实现

HotSpot虚拟机的src/share/vm/runtime/objectMonitor.hpp中有个ObjectMonitor的类揭示了monitor的底层实现。(源码点这里

  ObjectMonitor() {
    _header       = NULL;
    _count        = 0;
    _waiters      = 0,
    _recursions   = 0;
    _object       = NULL;
    _owner        = NULL;
    _WaitSet      = NULL;
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ;
    FreeNext      = NULL ;
    _EntryList    = NULL ;
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
    _previous_owner_tid = 0;
  }

Mesa语义和Hoare语义

假如有两个线程T1和T2,T1因为资源不满足进入了等待队列,这时T2释放了T1所需的资源,这时会发生什么呢?T1应该被立即唤醒,还是等T2结束呢?这种情景下的不同操作区分出了两种语义的管程——Mesa语义和Hoare语义。有关这两种语义的管程,本文不再赘述,详细可以参看UCSD的这篇讲义。至于Java具体使用的是哪种管程语义,作者找了很久资料也没有得出结果。

参考资料