线程管理

参见: http://harmony.apache.org/subcomponents/drlvm/TM.html

1. 修订历史

2. 关于本文档

2.1. 目的

2.2. 面向的读者

2.3. 文档约定

2.4. 文档使用

3. 概览

3.1. 主要特点

3.2. VM中的线程管理器

3.3. 可移植性

4. 体系结构

4.1. 对外接口

4.1.1. Native 接口

4.1.2. Java* 接口

4.2. 数据结构

4.3. 线程控制结构

4.3.1.Native 线程结构

4.3.2. Java* 线程结构

4.4. 线程组

4.5. 同步器

4.6. 监视器

4.6.1. 膨胀技术

4.6.2. 监视器结构

4.6.3. 获取一个监视器

5. 使用场景

5.1. Java* 线程生命周期

5.2. 线程挂起

5.3. Stop-the-world 线程挂起

5.4. 线程锁定

5.5. 监视器的进入和退出

6. 参考文献

修订历史

版本

版本信息

日期

初版

Nadya Morozova, Andrey Chernyshev: 创建文档.

2006年6月5日

关于本文档

目的

本文介绍了线程管理器组件,该组件是作为动态运行时层(Dynamic Runtime Layer, DRL)计划的一部分递交的。本文主要关注DRL虚拟机中线程管理器任务的当前实现细节,以及线程管理子系统的内部组织结构。

面向的读者

本文面向的读者包括对线程技术的进一步工作感兴趣,并且愿意贡献其开发的广大的工程师群体。本文假设读者对于DRLVM体系结构基础、线程方法学和结构都相当熟悉。

文档约定

本文使用对DRL文档包的统一约定。

文档使用

本文可以用来学习所有当前版本的实现细节。本文从多个方面描述线程管理器的功能,包括内部数据结构、体系结构细节以及线程管理器关键的使用场景。本文有如下主要部分:

  • 概览:给线程管理器组件及其在VM体系结构中的地位做了一个概括的定义。
  • 体系结构:描述了线程管理器的内部结构、数据结构和开放的接口。
  • 使用场景:展示了主要的线程相关操作,例如线程生命周期和线程挂起。
  • 参考文献:连接到与本文内容有关的材料。

概览

线程管理器(TM)是一个致力于为Java*虚拟机提供线程支持的库,它的目标是在操作系统提供的类POSIX线程模型和由J2SE规范建立的类Java*线程模型之间架设一座桥梁。

在当前的实现中,JVM线程子系统包括以下三层:

  • 与操作系统交互的移植层(porting layer)
  • 提供基本线程功能的本地层(native layer)
  • 与用户应用程序的Java*对象交互的Java*层(Java* layer)

需要注意的是:线程管理器包含该子系统的native layer和Java* layer;而porting layer在线程管理器外部。

每一层都为下层提供的线程支持增加某些功能。就是说,porting layer为操作系统提供的线程支持添加了可移植性(portability);native layer为porting layer提供的线程支持添加了Java*特定的增强;而Java* layer将Java*线程和对象连接到native layer,如图 1所示。这些接口被组织在一个头文件集合中,在下面的对外接口一节中描述。

主要特点

提供的线程管理器有如下特点:

  • 支持J2SE API[1|#ref1]、JVMTI[2|#ref2]和JNI[3|#ref3]规范要求的线程功能
  • 基于Apache Porting Layer的可移植实现[4|#ref4]
  • 遵从Harmony hythread 接口[8|#ref8]
  • 支持垃圾收集器
  • 特定于DRLVM提供的即时编译器(just-in-time, JIT)的监视器(monitor)优化

VM中的线程管理器

图 2 展示了线程管理器与以下的虚拟机组件之间的交互:

  • 虚拟机核心(VM core):通过它来访问对象的布局信息,从而将{{java.lang.Thread}}对象与适当的本地线程进行绑定。为此,线程管理器要查询虚拟机核心的{{thread_externals.h}}接口。
  • 垃圾收集器(garbage collector):通过它来满足垃圾收集器的根集枚举和垃圾收集活动的线程操作需求。GC和线程管理器的native layer一起工作。
  • 移植层(porting layer):通过它来与底层系统进行交互,保证线程支持的可移植性。线程管理器的native layer需要查询APR接口和{{apr_thread_ext}}接口的方法。
  • 即时编译器(just-in-time compiler):通过它来为JIT编译后的代码提供优化的线程支持功能,这些功能会被VM 帮助器调用。线程管理器通过Java* layer的{{thread_helpers}}接口开放这些功能。

{{

http://harmony.apache.org/subcomponents/drlvm/images/tm_in_vm.gif

}}

图 2. VM体系结构中的线程管理器

可移植性

线程管理器的代码大部分是平台无关的,依靠底层的porting layer适应特定的平台。当前的TM实现在Apache Porting Layer(APR)之上通过添加额外的扩展完成。平台相关的TM部分包括绑定到特定体系结构上的VM 帮助器包和部分绑定到OS API上的APR扩展包。

基于APR的移植使得线程管理器代码可以在任何APR可用的平台上进行编译。线程管理器的当前版本支持Linux*和Windows* IA-32平台。

体系结构

下面几节描述了线程管理器与其它VM组件交互时开放的功能接口及其内部数据结构。

对外接口

如概览所述,线程管理器对外提供native和Java*接口。这些接口表现为一组为外部请求提供特定功能的函数,在后面的节中描述。

Native接口

Native接口由Harmony hythread}}模块得到启发。这是一个低级的提供类Java*线程功能的层,例如对等待操作({{waitpark、{{join}}和{{sleep}}等)的中断支持。该接口帮助建立了线程与垃圾收集器间正确的交互。它并不处理Java*对象。

Native接口包含下列函数集:

hythread.h


{{hythread}}集合的函数[8|#ref8],其作用包括:

  • 基本操作
  • 暂停(Parking)
  • 线程本地存储支持
  • 读写互斥量支持
  • 监视器支持

hythread_ext.h


扩展{{hythread}}集合的一组函数,其作用包括:

  • 线程管理器的初始化和关闭
  • 线程组支持
  • 条件变量
  • 安全挂起支持
  • 闭锁(Latch )
  • 线程迭代器支持
  • 属性访问(Attributes access)
  • 线程的查询状态(Querying state of the thread )
  • 信号量
  • 互斥量
  • 瘦(thin)监视器支持
  • 查询线程状态 (Querying the thread state)
  • 线程属性访问(Thread attributes access)
  • 中断支持
  • 任务管理支持

Java* 接口

Java*接口将native layer提供的线程支持功能连接到Java*线程和对象。

Java*的接口函数把Java*对象作为参数,可以容易地用来实现内核类(kernel classes)、JNI或者JVMTI函数集合。Java*接口包括三部分:

jthread.h


支持{{java.lang.Object}}和{{java.lang.Thread}} API的函数,其作用包括:

  • 基本操作
  • 标识
  • 指针变换
  • 属性访问
  • 中断
  • 监视器
  • Parking
  • 挂起

ti_thread.h


支持不同JVMTI函数和{{java.lang.management}} API的函数,其作用包括:

  • 状态查询
  • 插桩
  • 本地存储
  • 监视器信息
  • CPU计时
  • 峰值(Peak)计数
  • 原始(Raw)监视器

thread_helpers.h


提供存根(stub)的函数。这些存根用于优化因线程管理器和JIT紧密地集成而带来的性能问题。

数据结构

线程管理器的数据结构一般是不暴露的,外部的VM组件只能通过透明指针访问这些数据结构。这些指针被定义在头文件{{hythread.h、hythread_ext.h、jthread.h}} 和 {{ti_thread.h}}中。数据结构本身在{{thread_private.h}}中描述。

线程控制结构

线程管理器要求每个线程在调用线程支持函数前要先*被注册(registered)。线程注册被称为系附一个线程(attaching a thread)*,这可用如下函数实现:

  • {{hythread_attach()}}函数在线程管理器中注册当前线程,从而线程支持操作可以借助native layer在此线程上执行。
  • {{jthread_attach()}}函数在当前线程和适当的{{java.lang.Thread}}对象间建立联系,从而线程支持操作可借助Java* layer在此线程上执行。

依赖于系附函数(attaching function),线程管理器操作两种类型的线程:

  • Native thread:被系附到线程管理器的native layer上的本地(native)线程。
  • Java* thread: 被系附到线程管理器Java* layer上,且与一个{{java.lang.Thread}}对象相关联的Java*线程。

每个线程类型有相关的结构,这个结构保存了线程特定的数据,在后面有所讲述。
其它VM组件使用操作那些结构的不透明句柄进行工作,而不能获得它们的内容信息。以这种方式与线程工作的时候,组件调用系附函数中的一个,获得该线程的线程控制结构的不透明句柄,然后就可以使用这个句柄对这个线城进行任何操作了。

Native 线程结构

当对线程管理器的native layer注册的时候,每个线程获得一个控制结构,这个结构包含所有执行线程操作所需的线程特定数据,例如:状态、属性、对特定OS线程结构的引用以及同步辅助等。这个控制结构随后为各种线程支持操作所使用。

注意:

线程控制结构的实际内容是与实现相关的,不暴露给其它组件。

下图展示了一个被{{HyThread}}类型描述的native系附线程的线程控制结构:

下表列出了组成native线程的线程控制结构的数据域:

数据域

描述

挂起(Suspension)

IDATA

suspend_request

导致本线程挂起请求的数量。

int16

suspend_disable_count

指示线程可以被安全挂起的标志。

hylatch_t

safe_region_event

当一个线程进入安全区时用来通知相关线程的事件。

hysem_t

resume_event

用来通知一个挂起线程需要唤醒的事件。

hythread_event_callback_proc

safepoint_callback

当恢复一个线程时在安全点上执行的函数。

基本操作域(Basic manipulation fields)

hythread_group_t

group

本线程所在的组等同于该组的线程列表的表头地址。线程组被用来快速地在线程列表上迭代。例如:Java*线程、GC私有线程。

hythread_t

next

指向线程组中下一个线程。

hythread_t

prev

指向线程组中上一个线程。

apr_thread_t *

os_handle

OS线程的句柄。

void *

private_data

任何本线程相关数据的占位符。Java* layer使用该域存储与Java有关的上下文信息。

Boolean

exit_request

指示是否已经收到一个退出请求的标志。

IDATA

exit_value

本线程的退出值。

同步支持(Synchronization support )

hysem_t

park_event

暂停(Parking)线程事件。

hysem_t

sleep_event

Sleeping线程事件。

hylatch_t

join_event

为调用join的线程保留的事件。

hycond_t

current_condition

当前这个线程等待的条件变量。用作中断。

状态(State)

IDATA

state

线程状态。持有JVMTI规范中定义的线程状态标志加上附加标志。

属性(Attributes)

char *

name

线程名,用作调试。

IDATA

priority

为调度器提供线程优先级信息。

IDATA

daemon

检查该线程是否为守护进程。

其它(Others)

IDATA

thread_id

该线程的ID。线程的最大数目由锁字记录(lock word record)的大小决定。欲了解锁字记录结构的细节,请参见监视器结构一节。

apr_pool_t *

pool

内存池,线程被分配在其中。

apr_threadattr_t *

apr_attrs

APR线程属性。

hythread_entrypoint_t

start_proc

描述了会被执行的线程体的过程。

void *

start_proc_args

被传递到线程体的参数。

void *

thread_local_storage

代表线程本地存储的数组。

void *

big_local_storage

对标准本地存储槽的扩展。

线程控制结构的细节参见源代码包中的{{thread_private.h}}头文件。

Java* 线程结构

Java*线程的线程控制结构由{{JVMTIThread}}类型定义。它持有特定于那个线程的大部分JVMTI信息,如图 4所示。

下表列出了组成Java*线程的线程控制结构的数据域:

数据域

描述

JNIEnv *

jenv

与该Java*线程关联的JNI环境变量。

jthread

thread_object

与hythread_t关联的jthread对象。

hycond_t

monitor_condition

用来wait/notify Java*监视器的条件变量。

jthrowable

stop_exception

被一个停止线程抛出的异常。

jlong

blocked_time

该线程在一个监视器上阻塞的时间,单位纳秒。

jlong

waited_time

该线程等待一个监视器的时间,单位纳秒。

JVMTILocalStorage

jvmti_local_storage

JVMTI本地存储。

jobject

contended_monitor

阻塞本线程的监视器。

jobject

wait_monitor

本线程正在等待的监视器。

jobject *

owned_monitors

本线程拥有的监视器。

int

owned_monitors_nmb

本线程拥有的监视器数量。

apr_pool_t *

pool

本结构体的APR池。

jobject

thread_ref

导致本线程挂起请求的数量。

IDATA

suspend_request

对{{java.lang.Thread}}实例的弱引用。

线程控制结构的细节参见源代码包中的{{thread_private.h}}头文件。

与系附到{{java.lang.Thread}}对象上的线程关联的数据结构以及独立(未系附)的线程分别在图 3和图 4中展示。

线程组

线程管理器使得多个线程组可以共存,例如,对Java*程序不可见的Java*线程组和GC线程组。每个被线程管理器维护的线程属于一个特定线程组,如图 5所示。

线程管理器提供一套函数用来在特定线程组中迭代遍历线程列表。所有线程被组织在一个线程组数组中,一个特定的系统范围的锁被用来防止对线程组数组以及组中线程列表的并发更改。在创建线程,删除线程以及迭代遍历线程列表时需要首先在内部申请这个锁。

同步器

线程管理器同步器是用于线程同步的功能模块。某些同步器有与其自身关联的内部数据结构,其它同步器只能向APR提供的适当的同步器委托函数调用。当前在线程管理器中实现的同步器是基于两个基本原语:条件变量和互斥量,如图 6所示。

图中的元素含义如下:

  • APR条件变量(APR conditional variable)和APR互斥量(APR mutex)是Apache Portable Runtime提供的基本原语。
  • TM条件变量(TM conditional variable)和TM互斥量(TM mutex)通过添加{{wait}}中断支持包装了相应的APR原语。这些同步器还保证,当使用条件变量将线程置为{{wait}}状态或者当线程正在获取一个互斥量时被阻塞,该线程会进入安全挂起模式(safe suspension mode)。
  • Thin监视器(Thin monitor)是一个结合了条件变量的膨胀锁(inflatable lock)。这种结合是构建Java*监视器的基础。
  • 信号量(Semaphore)与POSIX中的信号量相同,也允许指定计数限制。
  • Java*监视器(Java monitor)与{{java.lang.Object}}相同。
  • JVMTI 原始监视器(JVMTI raw monitor)是在JVMTI规范中定义的监视器。
  • 暂停(park)和中止暂停(unpark)锁支持原语用在{{java.util.concurrent}}包中。

上面的层次是为APR代码复用进行的优化。线程管理器有可能有其它的实现,这些实现可以利用APR同步器的不同集合。

注意:

线程管理器并不将同步器的内部结构暴露给外部组件。访问同步器都需要使用类似于线程控制结构的不透明句柄。

监视器

线程管理器的当前版本在实现Java*监视器的时候使用一种特殊的方法处理空间消耗的一般公共问题。DRL线程管理器提供一种特殊类型的监视器——thin监视器;这种监视器持有的锁为空间消耗和单线程的使用提供了优化。

膨胀技术

监视器膨胀用thin-fat锁技术[6|#ref6]实现,工作过程如下:

  • 在没有线程竞争的情况下,锁数据被存储在若干字节当中,从而可以在分配Java*对象的同时直接分配锁。
  • 无论竞争何时发生,为锁数据分配的若干字节保存一个对fat锁的引用,这个fat锁可以是一个传统的互斥量。

thin监视器的不同实现可以自由选择空间紧缩或其它优化技术(或并不优化)。但是,推荐的做法是当需要节省内存并且线程竞争不是很激烈时就应选用thin监视器。另外推荐的做法是,使用传统的互斥量和条件变量可以在高竞争的情况下获得更好的可伸缩性。线程管理器中的Java*监视器建立在thin监视器之上。这使得线程管理器可以在Java*对象中直接为thin监视器分配锁结构,而且因此Java*监视器的空间利用率大大提高。

监视器结构

thin监视器是一个实现了锁压缩技术的同步器原语,它可以作为建立Java*监视器[6]的基础。换句话说,thin监视器驻留在TM子系统的native layer,不在Java*对象存放任何数据。Java*监视器与Java*对象是紧耦合的,驻留在TM子系统更高的Java* layer。

同步器的核心内容是*锁字(lock word)*。锁字持有thin锁值或者对fat锁的引用,这依赖于竞争情况。

在没有竞争的情况下,锁的类型是0,锁字有如下结构:

  • Contention bit:0表示没有竞争;
  • Thread ID (15 bits): 持有该锁的线程ID。如果锁未被任何线程持有,则该位为0;
  • Recursion count: 锁被同一个线程反复申请的次数减1;
  • Reservation bit: 指示锁是否被一个线程保留的标志[7|#ref7];
  • 最右边的10 bits在TM中未使用,它们被保留用作存储Java*对象的哈希码。

在有竞争的情况下,contention bit被设置为1,一个thin压缩的锁变成了一个fat膨胀锁,它有如下布局:

  • Contention bit: 1表示存在竞争;
  • Fat Lock ID (20 bits): 相应fat锁的ID;
  • Reservation bit: 指示锁是否被一个线程保留的标志[7|#ref7];
  • 最右边的10 bits在TM中未使用,它们被保留用作存储Java*对象的哈希码。

线程管理器用一个张全局锁表实现(译注:fat)锁ID和相关fat监视器之间的映射,如下:

获取一个监视器

借助{{hythread_thin_monitor_try_enter()}}函数可以获取一个监视器,其过程如下图:

首先,线程通过reservation bit检查所需的锁是否已被自己拥有。如果是,线程将recursion count加1,函数退出。这种做法使得单线程程序拥有最快速的monitor enter操作路径。这条快速路径只包含若干汇编指令,而没有高开销的原子比较-交换(compare-and-swap, CAS)操作。

如果锁还没有被保留,检查它的占用情况。如果锁是未被占用的(lock free),将它设置为“已保留”同时以一条CAS操作获得之。如果锁之后变成被占用的(lock busy),系统检查锁是否为fat锁。

锁表持有fat锁ID和实际监视器之间的映射关系。Fat监视器被从锁表中提取出来并被获取。如果锁不是fat并且被另一个线程保留,那么申请锁的线程挂起锁持有者线程,去除保留状态,然后再启动锁持有者线程。之后,再重新尝试获取锁。

使用场景

这一节包括各种线程操作的场景。

Java* 线程生命期

Java*线程的创建过程包括如下主要步骤:

  1. 在创建了一个线程之后,Thread()}}构造函数创建一个新的native线程并初始化线程控制结构{{HyThread}}和{{JVMTIThread。 1. 用户应用程序然后调用VM core组件中内核类的{{java.lang.Thread.start()}}方法。 1. {{java.lang.Thread.start()}}方法通过{{java.lang.VMThreadManager.start()}}函数向线程管理器中的Java* layer的{{jthread_create()}}函数委托一个调用。 1. jthread_create()}}函数调用{{jthread_create_with_function()。 1. {{jthread_create_with_function()}}函数调用{{hythread_create()}}函数,提供{{wrapper_proc}}作为一个新线程体的过程。
    1. {{hythread_create()}}函数向APR porting layer的{{apr_thread_create()}}函数委托一个调用,它本身并不真正{{fork并创建一个新线程。在这一步,提供{{{wrapper_proc}}参数作为线程体。
    1. 新创建的线程开始执行{{thread_start_proc()}},这个函数再转而执行{{wrapper_proc()}}函数。
    1. 函数{{wrapper_proc()}}执行新线程向其它组件的注册请求。方法是通过向VM core发送{{vm_attach}}事件,并调用{{jvmti_send_thread_start_end_event()}}函数,它会发送{{JVMTI_EVENT_THREAD_START}}事件。
    1. {{wrapper_proc()}}函数调用{{java.lang.Thead.run()}}方法,这个方法即是用户定义的新Java*线程的线程体。
    1. 在{{Thread.run()}}结束之后,线程向其它组件注销。方法是调用{{jvmti_send_thread_start_end_event()}}函数会,它会先发送{{JVMTI_EVENT_THREAD_END}}事件,然后是{{vm_detach}}事件。

下图展示了线程创建和完成的详细过程:

注意:

native线程控制结构(例如,HyThread}}和{{JVMTIThread)并不是在每次新线程结束时都被销毁。线程管理器为每个{{java.lang.Thread}}对象创建一个弱引用以提供内部引用队列。当一个特定的{{java.lang.Thread}}对象被垃圾回收时,垃圾收集器在那个队列中放置一个引用。在为新线程分配native资源之前,线程管理器在队列中寻找弱引用。一旦弱引用队列非空,线程管理器从队列中提取第一个可用引用并为新建线程复用那个引用的native资源。

线程挂起

native layer为APR线程支持添加的一个主要特征是安全挂起(safe suspension)。该机制保证了挂起的线程在活引用枚举期间可以被垃圾收集器安全地探测到。如果线程持有一些系统关键锁,如与本地堆内存关联的锁,安全挂起可以使得这些线程在枚举期间仍然运行。否则,一旦VM的其它部分需要同样的系统锁,使用系统调用或者“hard”调用来挂起线程可能导致死锁。

安全挂起的算法描述了两个线程间的通信协议,例如,有两个线程T1和T2,其中T1安全挂起线程T2。T1调用{{hythread_suspend(T2)}}函数来挂起线程T2。该过程步骤如下:

1. {{hythread_suspend(T2)}}函数为线程T2增加指示请求挂起的标志。依赖于T2当前的状态,{{hythread_suspend(T2)}}函数执行下面机制中的一个:
a. 如果线程T2当前运行在安全代码区,{{hythread_suspend(T2)}}调用立即返回,如图 12。
a. 如果线程T2当前运行在非安全代码区,{{hythread_suspend()}}在T2到达安全区开始点或者安全点之前都会阻塞。
1. 线程T2运行到安全区结束点,然后阻塞直到T1调用{{hythread_resume(T2)}}恢复它。

线程T2经历如下:

1. 线程T2周期性地调用{{hythread_safe_point()}}函数指定安全挂起点。如果之前有对T2的挂起请求,该方法通知T1然后等待直到T1调用{{hythread_resume(T2)}}恢复T2。
1. 当T2进入安全区,它调用{{hythread_suspend_ensable()}}方法,该方法减少{{suspend_disable_count}}状态标志。如果之前有对T2的挂起请求,T1会得到T2已经到达安全区的通知。
1. 当T2离开安全区,它调用{{hythread_suspend_disable()}}函数,该函数增加{{suspend_disable_count}}状态标志。

一个典型的安全挂起场景的例子发生在垃圾收集器挂起一个Java*线程以便进行活引用枚举的时候。图 12 展示了GC使用线程管理器挂起运行在安全区的Java*线程的情况。

为了更好地理解安全线程挂起算法,考虑每个线程都有一个锁与之关联。当线程T2进入安全区时释放锁,离开安全区时获取锁。为挂起线程T2,需要获取与之关联的锁。恢复T2等同于释放与之关联的锁。安全挂起算法的一个直观的实现为每个线程保留一个单线程优化的锁(即:thin监视器)并且使用它来挂起和恢复那个线程。

另一个安全挂起情况是当一个GC线程碰到一个运行在非安全代码区的Java*线程,如图13所示。

考虑{{hythread_safe_point()}}作为一个在和线程关联的监视器上执行的{{wait}}操作。在这种情况下,{{hythread_resume()}}等同于通知那个监视器。

Stop-the-world 线程挂起

当垃圾收集器需要为某个线程组中所有线程枚举活对象引用时,将发生stop-the-world线程挂起。图 14 展示了当只有一个GC线程但是有无数Java*线程在运行时的情况,此时GC需要挂起所有Java*线程。

首先,垃圾收集器调用线程管理器接口函数{{hythread_suspend_all()}}以便挂起运行在给定线程组中的每一线程(在本示例中,所有Java*线程)。线程管理器随后返回用于遍历被挂起线程列表的迭代器。GC使用该迭代器分析每一Java*线程与活引用的关系,然后进行垃圾回收。完成后,GC通知线程管理器恢复所有线程。

线程锁定

涉及线程管理器的锁定可以通过互斥量或者thin监视器的方法实现。互斥量适合用在高度竞争的情况下,而thin监视器对空间进行了更好的优化。本节描述VM core试图锁定来自多个线程T1和T2的资源。锁定和解锁的主要过程如图 15所示。

开始,互斥量未被占用,就是说,标签锁被设置为0。线程T1调用{{hymutex_lock()}}函数,该函数通知线程管理器标记互斥量表示已经被T1锁定了。

T2也可以在随后调用{{hymutex_lock()}},如果刚好所需的锁已经被占用,T2被放置在内部与互斥量关联的等待队列中,并阻塞直到T1对该互斥量解锁。T1调用{{hymutex_unlock()}}释放互斥量,这会使得互斥量从等待队列中提取T2,将锁的所有权授予T2,然后通知T2可以醒来了。

监视器的进入和退出

锁定Java*监视器意味着在线程管理器和VM core之间的交互,因为线程管理器需要请求Java*对象中的内存地址,其中保存锁数据。锁定Java*监视器的过程如图 16所示。

如果Java*代码中有同步片段,会有以下步骤:

1. JIT生成的代码调用线程管理器提供的{{hythread_monitor_enter()}}帮助器函数。帮助器函数提供一个代码块(存根),该存根可被JIT直接内联到所生成的汇编码中。
1. {{hythread_monitor_enter()}}帮助器调用VM core组件的{{vm_object_get_lockword_addr()}}函数来查找Java*对象中锁字的物理地址。
1. 帮助器调用{{thin_monitor_try_lock()}}函数以便获取对象关联的锁。
1. 一旦锁被获取,则帮助器返回。这个获取Java*监视器的快速路径。在这个场景下,帮助器不需要在Java*和native帧之间切换,{{thin_monitor_try_enter()}}会被直接调用,不涉及任何Java*对象。否则,帮助器代码通过在Java*和native代码之间切换而进入一个慢速路径(参见下图中压入一个{{M2nFrame}}并创建本地句柄的动作)。
1. 帮助器调用{{jthread_monitor_enter()}}函数,该函数就像在JNI代码一样与Java*对象协同工作。

参考文献

[1] J2SE 1.5.0 specification, http://java.sun.com/j2se/1.5.0/docs/api/ <ac:structured-macro ac:name="anchor" ac:schema-version="1" ac:macro-id="459531ce-7263-4085-ad05-957f824b01a4"><ac:parameter ac:name="">ref1</ac:parameter></ac:structured-macro>

[2] JVM Tool Interface Specification, http://java.sun.com/j2se/1.5.0/docs/guide/jvmti/jvmti.html <ac:structured-macro ac:name="anchor" ac:schema-version="1" ac:macro-id="529737f8-b1a9-4890-8287-71e333534938"><ac:parameter ac:name="">ref2</ac:parameter></ac:structured-macro>

[3] Java* Native Interface Specification, http://java.sun.com/j2se/1.5.0/docs/guide/jni/spec/jniTOC.html <ac:structured-macro ac:name="anchor" ac:schema-version="1" ac:macro-id="c5f33b48-8d2b-4fe2-937b-049e6be92ffe"><ac:parameter ac:name="">ref3</ac:parameter></ac:structured-macro>

[4] Apache Portable Runtime project, http://apr.apache.org/ <ac:structured-macro ac:name="anchor" ac:schema-version="1" ac:macro-id="ca6282e2-ea86-466a-8098-751f04cabc4d"><ac:parameter ac:name="">ref4</ac:parameter></ac:structured-macro>

[5] POSIX standard in threading, http://www.opengroup.org/onlinepubs/009695399/idx/threads.html <ac:structured-macro ac:name="anchor" ac:schema-version="1" ac:macro-id="812eec82-c8ed-4807-8b26-05ecef4d25d9"><ac:parameter ac:name="">ref5</ac:parameter></ac:structured-macro>

[6] David F. Bacon, Ravi Konuru, Chet Murthy, Mauricio Serrano, Thin locks: featherweight synchronization for Java, http://portal.acm.org/citation.cfm?id=277734 <ac:structured-macro ac:name="anchor" ac:schema-version="1" ac:macro-id="97d5eb6e-359b-4b9d-a1ae-1eb4a9dfc380"><ac:parameter ac:name="">ref6</ac:parameter></ac:structured-macro>

[7] Kiyokuni Kawachiya Akira Koseki Tamiya Onodera, Lock Reservation: Java Locks Can Mostly Do Without Atomic Operation, http://portal.acm.org/citation.cfm?id=582433 <ac:structured-macro ac:name="anchor" ac:schema-version="1" ac:macro-id="75c17642-c5f1-44c7-b55b-ec82e3259490"><ac:parameter ac:name="">ref7</ac:parameter></ac:structured-macro>

[8] [HyThread] documentation, http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/doc/vm_doc/html/group__Thread.html <ac:structured-macro ac:name="anchor" ac:schema-version="1" ac:macro-id="73972c0b-4e74-4c59-b427-3b7a33d37c84"><ac:parameter ac:name="">ref8</ac:parameter></ac:structured-macro>

  • Other brands and names are the property of their respective owners.

(Contributors: 赵雷(asuka@ustc.edu)、张昱(yuzhang@ustc.edu.cn))

  • No labels