Differences between revisions 15 and 16
Revision 15 as of 2006-12-27 12:41:38
Size: 34589
Editor: XiaoFeng Li
Comment:
Revision 16 as of 2009-09-20 21:54:51
Size: 34894
Editor: localhost
Comment: converted to 1.6 markup
Deletions are marked like this. Additions are marked like this.
Line 7: Line 7:
[#修订历史 1. 修订历史]

[#关于本文档 2. 关于本文档]

 
[#目的 2.1. 目的]

 
[#面向的读者 2.2. 面向的读者]

 
[#文档约定 2.3. 文档约定]

 
[#文档使用 2.4. 文档使用]

[#概览 3. 概览]

 
[#主要特点 3.1. 主要特点]

 
[#VM中的线程管理器 3.2. VM中的线程管理器]

 
[#可移植性 3.3. 可移植性]

[#体系结构 4. 体系结构]

 
[#对外接口 4.1. 对外接口]

  
[#Native_接口 4.1.1. Native 接口]

  
[#Java*_接口 4.1.2. Java* 接口]

 
[#数据结构 4.2. 数据结构]

 
[#线程控制结构 4.3. 线程控制结构]

  
[#Native_线程结构 4.3.1.Native 线程结构]

  
[#Java*_线程结构 4.3.2. Java* 线程结构]

 
[#线程组 4.4. 线程组]

 
[#同步器 4.5. 同步器]

 
[#监视器 4.6. 监视器]

  
[#膨胀技术 4.6.1. 膨胀技术]

  
[#监视器结构 4.6.2. 监视器结构]

  
[#获取一个监视器 4.6.3. 获取一个监视器]

[#使用场景 5. 使用场景]

 
[#Java*_线程生命周期 5.1. Java* 线程生命周期]

 
[#线程挂起 5.2. 线程挂起]

 
[#Stop_the_world_线程挂起  5.3. Stop-the-world 线程挂起]

 
[#线程锁定 5.4. 线程锁定]

 
[#监视器的进入和退出 5.5. 监视器的进入和退出]

[#参考文献 6. 参考文献]


[[
Anchor(修订历史)]]
[[#修订历史|1. 修订历史]]

[
[#关于本文档|2. 关于本文档]]

 [
[#目的|2.1. 目的]]

 [
[#面向的读者|2.2. 面向的读者]]

 [
[#文档约定|2.3. 文档约定]]

 [
[#文档使用|2.4. 文档使用]]

[
[#概览|3. 概览]]

 [
[#主要特点|3.1. 主要特点]]

 [
[#VM中的线程管理器|3.2. VM中的线程管理器]]

 [
[#可移植性|3.3. 可移植性]]

[
[#体系结构|4. 体系结构]]

 [
[#对外接口|4.1. 对外接口]]

  [
[#Native_接口|4.1.1. Native 接口]]

  [
[#Java*_接口|4.1.2. Java* 接口]]

 [
[#数据结构|4.2. 数据结构]]

 [
[#线程控制结构|4.3. 线程控制结构]]

  [
[#Native_线程结构|4.3.1.Native 线程结构]]

  [
[#Java*_线程结构|4.3.2. Java* 线程结构]]

 [
[#线程组|4.4. 线程组]]

 [
[#同步器|4.5. 同步器]]

 [
[#监视器|4.6. 监视器]]

  [
[#膨胀技术|4.6.1. 膨胀技术]]

  [
[#监视器结构|4.6.2. 监视器结构]]

  [
[#获取一个监视器|4.6.3. 获取一个监视器]]

[
[#使用场景|5. 使用场景]]

 [
[#Java*_线程生命周期|5.1. Java* 线程生命周期]]

 [
[#线程挂起|5.2. 线程挂起]]

 [
[#Stop_the_world_线程挂起|5.3. Stop-the-world 线程挂起]]

 [
[#线程锁定|5.4. 线程锁定]]

 [
[#监视器的进入和退出|5.5. 监视器的进入和退出]]

[
[#参考文献|6. 参考文献]]


<<
Anchor(修订历史)>>
Line 75: Line 75:
[[Anchor(关于本文档)]] <<Anchor(关于本文档)>>
Line 78: Line 78:
[[Anchor(目的)]] <<Anchor(目的)>>
Line 83: Line 83:
[[Anchor(面向的读者)]] <<Anchor(面向的读者)>>
Line 88: Line 88:
[[Anchor(文档约定)]] <<Anchor(文档约定)>>
Line 93: Line 93:
[[Anchor(文档使用)]] <<Anchor(文档使用)>>
Line 98: Line 98:
 * [#概览 概览]:给线程管理器组件及其在VM体系结构中的地位做了一个概括的定义。
 * [#体系结构 体系结构]:描述了线程管理器的内部结构、数据结构和开放的接口。
 * [#使用场景 使用场景]:展示了主要的线程相关操作,例如线程生命周期和线程挂起。
 * [#参考文献 参考文献]:连接到与本文内容有关的材料。

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

<<Anchor(概览)>>
Line 106: Line 106:
线程管理器(TM)是一个致力于为Java[#note *]虚拟机提供线程支持的库,它的目标是在操作系统提供的类POSIX线程模型和由J2SE规范建立的类Java[#note *]线程模型之间架设一座桥梁。 线程管理器(TM)是一个致力于为Java[[#note|*]]虚拟机提供线程支持的库,它的目标是在操作系统提供的类POSIX线程模型和由J2SE规范建立的类Java[[#note|*]]线程模型之间架设一座桥梁。
Line 110: Line 110:
 * 与操作系统交互的[#可移植性 移植层](porting layer)
 * 提供基本线程功能的[#Native接口 本地层](native layer)
 * 与用户应用程序的Java[#note *]对象交互的[#Java*_接口 Java*层](Java[#note *] layer)
 
需要注意的是:线程管理器包含该子系统的native layer和Java[#note *] layer;而porting layer在线程管理器外部。

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

||<:tablewidth="100%">http://harmony.apache.org/subcomponents/drlvm/images/ThreadingSystem.gif||
 * 与操作系统交互的[[#可移植性|移植层]](porting layer)
 * 提供基本线程功能的[[#Native接口|本地层]](native layer)
 * 与用户应用程序的Java[[#note|*]]对象交互的[[#Java*_接口|Java*层]](Java[[#note|*]] layer)
 
需要注意的是:线程管理器包含该子系统的native layer和Java[[#note|*]] layer;而porting layer在线程管理器外部。

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

||<:tablewidth="100%">{{http://harmony.apache.org/subcomponents/drlvm/images/ThreadingSystem.gif}}||
Line 121: Line 121:
[[Anchor(主要特点)]] <<Anchor(主要特点)>>
Line 126: Line 126:
 * 支持J2SE API[[#ref1 1]]、JVMTI[[#ref2 2]]和JNI[[#ref3 3]]规范要求的线程功能
 * 基于Apache Porting Layer的可移植实现[[#ref4 4]]
 * 遵从Harmony {{{hythread}}} 接口[[#ref8 8]]
 * 支持J2SE API[[[#ref1|1]]]、JVMTI[[[#ref2|2]]]和JNI[[[#ref3|3]]]规范要求的线程功能
 * 基于Apache Porting Layer的可移植实现[[[#ref4|4]]]
 * 遵从Harmony {{{hythread}}} 接口[[[#ref8|8]]]
Line 132: Line 132:
[[Anchor(VM中的线程管理器)]] <<Anchor(VM中的线程管理器)>>
Line 139: Line 139:
 * '''移植层(porting layer)''':通过它来与底层系统进行交互,保证线程支持的[#可移植性 可移植性]。线程管理器的native layer需要查询APR接口和{{{apr_thread_ext}}}接口的方法。
 * '''即时编译器(just-in-time compiler)''':通过它来为JIT编译后的代码提供优化的线程支持功能,这些功能会被VM 帮助器调用。线程管理器通过Java[#note *] layer的{{{thread_helpers}}}接口开放这些功能。

||<:tablewidth="100%">http://harmony.apache.org/subcomponents/drlvm/images/tm_in_vm.gif||
 * '''移植层(porting layer)''':通过它来与底层系统进行交互,保证线程支持的[[#可移植性|可移植性]]。线程管理器的native layer需要查询APR接口和{{{apr_thread_ext}}}接口的方法。
 * '''即时编译器(just-in-time compiler)''':通过它来为JIT编译后的代码提供优化的线程支持功能,这些功能会被VM 帮助器调用。线程管理器通过Java[[#note|*]] layer的{{{thread_helpers}}}接口开放这些功能。

||<:tablewidth="100%">{{http://harmony.apache.org/subcomponents/drlvm/images/tm_in_vm.gif}}||
Line 145: Line 145:
[[Anchor(可移植性)]] <<Anchor(可移植性)>>
Line 150: Line 150:
基于APR的移植使得线程管理器代码可以在任何APR可用的平台上进行编译。线程管理器的当前版本支持Linux[#note *]和Windows[#note *] IA-32平台。

[[Anchor(体系结构)]]
基于APR的移植使得线程管理器代码可以在任何APR可用的平台上进行编译。线程管理器的当前版本支持Linux[[#note|*]]和Windows[[#note|*]] IA-32平台。

<<Anchor(体系结构)>>
Line 157: Line 157:
[[Anchor(对外接口)]] <<Anchor(对外接口)>>
Line 160: Line 160:
如概览所述,线程管理器对外提供native和Java[#note *]接口。这些接口表现为一组为外部请求提供特定功能的函数,在后面的节中描述。

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

<<Anchor(Native接口)>>
Line 165: Line 165:
Native接口由Harmony {{{hythread}}}模块得到启发。这是一个低级的提供类Java[#note *]线程功能的层,例如对等待操作({{{wait}}}、{{{park}}}、{{{join}}}和{{{sleep}}}等)的中断支持。该接口帮助建立了线程与垃圾收集器间正确的交互。它并不处理Java[#note *]对象。 Native接口由Harmony {{{hythread}}}模块得到启发。这是一个低级的提供类Java[[#note|*]]线程功能的层,例如对等待操作({{{wait}}}、{{{park}}}、{{{join}}}和{{{sleep}}}等)的中断支持。该接口帮助建立了线程与垃圾收集器间正确的交互。它并不处理Java[[#note|*]]对象。
Line 171: Line 171:
{{{hythread}}}集合的函数[[#ref8 8]],其作用包括: {{{hythread}}}集合的函数[[[#ref8|8]]],其作用包括:
Line 198: Line 198:
[[Anchor(Java*_接口)]] <<Anchor(Java*_接口)>>
Line 201: Line 201:
Java[#note *]接口将native layer提供的线程支持功能连接到Java[#note *]线程和对象。

Java[#note *]的接口函数把Java[#note *]对象作为参数,可以容易地用来实现内核类(kernel classes)、JNI或者JVMTI函数集合。Java[#note *]接口包括三部分:
Java[[#note|*]]接口将native layer提供的线程支持功能连接到Java[[#note|*]]线程和对象。

Java[[#note|*]]的接口函数把Java[[#note|*]]对象作为参数,可以容易地用来实现内核类(kernel classes)、JNI或者JVMTI函数集合。Java[[#note|*]]接口包括三部分:
Line 233: Line 233:
[[Anchor(数据结构)]] <<Anchor(数据结构)>>
Line 238: Line 238:
[[Anchor(线程控制结构)]] <<Anchor(线程控制结构)>>
Line 244: Line 244:
 * {{{jthread_attach()}}}函数在当前线程和适当的{{{java.lang.Thread}}}对象间建立联系,从而线程支持操作可借助Java[#note *] layer在此线程上执行。  * {{{jthread_attach()}}}函数在当前线程和适当的{{{java.lang.Thread}}}对象间建立联系,从而线程支持操作可借助Java[[#note|*]] layer在此线程上执行。
Line 249: Line 249:
 * Java[#note *] thread: 被系附到线程管理器Java[#note *] layer上,且与一个{{{java.lang.Thread}}}对象相关联的Java[#note *]线程。  * Java[[#note|*]] thread: 被系附到线程管理器Java[[#note|*]] layer上,且与一个{{{java.lang.Thread}}}对象相关联的Java[[#note|*]]线程。
Line 254: Line 254:
[[Anchor(Native_线程结构)]] <<Anchor(Native_线程结构)>>
Line 265: Line 265:
||<:tablewidth="100%">http://harmony.apache.org/subcomponents/drlvm/images/NativeUnattachedThread.gif|| ||<:tablewidth="100%">{{http://harmony.apache.org/subcomponents/drlvm/images/NativeUnattachedThread.gif}}||
Line 278: Line 278:
||{{{hythread_group_t}}}||{{{group}}}||本线程所在的组等同于该组的线程列表的表头地址。线程组被用来快速地在线程列表上迭代。例如:Java[#note *]线程、GC私有线程。|| ||{{{hythread_group_t}}}||{{{group}}}||本线程所在的组等同于该组的线程列表的表头地址。线程组被用来快速地在线程列表上迭代。例如:Java[[#note|*]]线程、GC私有线程。||
Line 282: Line 282:
||{{{void *}}}||{{{private_data}}}||任何本线程相关数据的占位符。Java[#note *] layer使用该域存储与Java有关的上下文信息。|| ||{{{void *}}}||{{{private_data}}}||任何本线程相关数据的占位符。Java[[#note|*]] layer使用该域存储与Java有关的上下文信息。||
Line 307: Line 307:
[[Anchor(Java*_线程结构)]] <<Anchor(Java*_线程结构)>>
Line 310: Line 310:
Java[#note *]线程的线程控制结构由{{{JVMTIThread}}}类型定义。它持有特定于那个线程的大部分JVMTI信息,如图 4所示。

||<:tablewidth="100%">http://harmony.apache.org/subcomponents/drlvm/images/JavaAttached.gif||
||<:>图 4. Java[#note *] 系附的线程||
 
下表列出了组成Java[#note *]线程的线程控制结构的数据域:
Java[[#note|*]]线程的线程控制结构由{{{JVMTIThread}}}类型定义。它持有特定于那个线程的大部分JVMTI信息,如图 4所示。

||<:tablewidth="100%">{{http://harmony.apache.org/subcomponents/drlvm/images/JavaAttached.gif}}||
||<:>图 4. Java[[#note|*]] 系附的线程||
 
下表列出了组成Java[[#note|*]]线程的线程控制结构的数据域:
Line 318: Line 318:
||{{{JNIEnv *}}}||{{{jenv}}}||与该Java[#note *]线程关联的JNI环境变量。|| ||{{{JNIEnv *}}}||{{{jenv}}}||与该Java[[#note|*]]线程关联的JNI环境变量。||
Line 320: Line 320:
||{{{hycond_t}}}||{{{monitor_condition}}}||用来wait/notify Java[#note *]监视器的条件变量。|| ||{{{hycond_t}}}||{{{monitor_condition}}}||用来wait/notify Java[[#note|*]]监视器的条件变量。||
Line 337: Line 337:
[[Anchor(线程组)]] <<Anchor(线程组)>>
Line 340: Line 340:
线程管理器使得多个线程组可以共存,例如,对Java[#note *]程序不可见的Java[#note *]线程组和GC线程组。每个被线程管理器维护的线程属于一个特定线程组,如图 5所示。

||<:tablewidth="100%">http://harmony.apache.org/subcomponents/drlvm/images/thread_groups.gif||
线程管理器使得多个线程组可以共存,例如,对Java[[#note|*]]程序不可见的Java[[#note|*]]线程组和GC线程组。每个被线程管理器维护的线程属于一个特定线程组,如图 5所示。

||<:tablewidth="100%">{{http://harmony.apache.org/subcomponents/drlvm/images/thread_groups.gif}}||
Line 347: Line 347:
[[Anchor(同步器)]] <<Anchor(同步器)>>
Line 352: Line 352:
||<:tablewidth="100%">http://harmony.apache.org/subcomponents/drlvm/images/Synchronizer_mutex.gif|| ||<:tablewidth="100%">{{http://harmony.apache.org/subcomponents/drlvm/images/Synchronizer_mutex.gif}}||
Line 358: Line 358:
 * [#Thin监视器结构 Thin监视器](Thin monitor)是一个结合了条件变量的膨胀锁(inflatable lock)。这种结合是构建Java[#note *]监视器的基础。  * [[#Thin监视器结构|Thin监视器]](Thin monitor)是一个结合了条件变量的膨胀锁(inflatable lock)。这种结合是构建Java[[#note|*]]监视器的基础。
Line 360: Line 360:
 * Java[#note *]监视器(Java monitor)与{{{java.lang.Object}}}相同。  * Java[[#note|*]]监视器(Java monitor)与{{{java.lang.Object}}}相同。
Line 371: Line 371:
[[Anchor(监视器)]] <<Anchor(监视器)>>
Line 374: Line 374:
线程管理器的当前版本在实现Java[#note *]监视器的时候使用一种特殊的方法处理空间消耗的一般公共问题。DRL线程管理器提供一种特殊类型的监视器——''thin监视器'';这种监视器持有的锁为空间消耗和单线程的使用提供了优化。

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

<<Anchor(膨胀技术)>>
Line 379: Line 379:
监视器膨胀用thin-fat锁技术[[#ref6 6]]实现,工作过程如下:

 * 在没有线程竞争的情况下,锁数据被存储在若干字节当中,从而可以在分配Java[#note *]对象的同时直接分配锁。
监视器膨胀用thin-fat锁技术[[[#ref6|6]]]实现,工作过程如下:

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

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

<<Anchor(监视器结构)>>
Line 389: Line 389:
thin监视器是一个实现了锁压缩技术的同步器原语,它可以作为建立Java[#note *]监视器[#ref6 [6]]的基础。换句话说,thin监视器驻留在TM子系统的native layer,不在Java[#note *]对象存放任何数据。Java[#note *]监视器与Java[#note *]对象是紧耦合的,驻留在TM子系统更高的Java[#note *] layer。 thin监视器是一个实现了锁压缩技术的同步器原语,它可以作为建立Java[[#note|*]]监视器[[#ref6|[6]]]的基础。换句话说,thin监视器驻留在TM子系统的native layer,不在Java[[#note|*]]对象存放任何数据。Java[[#note|*]]监视器与Java[[#note|*]]对象是紧耦合的,驻留在TM子系统更高的Java[[#note|*]] layer。
Line 395: Line 395:
||<:tablewidth="100%">http://harmony.apache.org/subcomponents/drlvm/images/uninflated_lockword.gif|| ||<:tablewidth="100%">{{http://harmony.apache.org/subcomponents/drlvm/images/uninflated_lockword.gif}}||
Line 401: Line 401:
 * Reservation bit: 指示锁是否被一个线程保留的标志[[#ref7 7]];
 * 最右边的10 bits在TM中未使用,它们被保留用作存储Java[#note *]对象的哈希码。
 * Reservation bit: 指示锁是否被一个线程保留的标志[[[#ref7|7]]];
 * 最右边的10 bits在TM中未使用,它们被保留用作存储Java[[#note|*]]对象的哈希码。
Line 406: Line 406:
||<:tablewidth="100%">http://harmony.apache.org/subcomponents/drlvm/images/inflated_lockword.gif|| ||<:tablewidth="100%">{{http://harmony.apache.org/subcomponents/drlvm/images/inflated_lockword.gif}}||
Line 411: Line 411:
 * Reservation bit: 指示锁是否被一个线程保留的标志[[#ref7 7]];
 * 最右边的10 bits在TM中未使用,它们被保留用作存储Java[#note *]对象的哈希码。
 * Reservation bit: 指示锁是否被一个线程保留的标志[[[#ref7|7]]];
 * 最右边的10 bits在TM中未使用,它们被保留用作存储Java[[#note|*]]对象的哈希码。
Line 416: Line 416:
||<:tablewidth="100%">http://harmony.apache.org/subcomponents/drlvm/images/inflated_thin_monitor.gif|| ||<:tablewidth="100%">{{http://harmony.apache.org/subcomponents/drlvm/images/inflated_thin_monitor.gif}}||
Line 419: Line 419:
[[Anchor(获取一个监视器)]] <<Anchor(获取一个监视器)>>
Line 424: Line 424:
||<:tablewidth="100%">http://harmony.apache.org/subcomponents/drlvm/images/Lock_reservation.gif|| ||<:tablewidth="100%">{{http://harmony.apache.org/subcomponents/drlvm/images/Lock_reservation.gif}}||
Line 433: Line 433:
[[Anchor(使用场景)]] <<Anchor(使用场景)>>
Line 438: Line 438:
[[Anchor(Java*_线程生命期)]] <<Anchor(Java*_线程生命期)>>
Line 441: Line 441:
Java[#note *]线程的创建过程包括如下主要步骤:

 1. 在创建了一个线程之后,{{{Thread()}}}构造函数创建一个新的native线程并初始化[#线程控制结构 线程控制结构]{{{HyThread}}}和{{{JVMTIThread}}}。
Java[[#note|*]]线程的创建过程包括如下主要步骤:

 1. 在创建了一个线程之后,{{{Thread()}}}构造函数创建一个新的native线程并初始化[[#线程控制结构|线程控制结构]]{{{HyThread}}}和{{{JVMTIThread}}}。
Line 445: Line 445:
 1. {{{java.lang.Thread.start()}}}方法通过{{{java.lang.VMThreadManager.start()}}}函数向线程管理器中的Java[#note *] layer的{{{jthread_create()}}}函数委托一个调用。  1. {{{java.lang.Thread.start()}}}方法通过{{{java.lang.VMThreadManager.start()}}}函数向线程管理器中的Java[[#note|*]] layer的{{{jthread_create()}}}函数委托一个调用。
Line 451: Line 451:
 1. {{{wrapper_proc()}}}函数调用{{{java.lang.Thead.run()}}}方法,这个方法即是用户定义的新Java[#note *]线程的线程体。  1. {{{wrapper_proc()}}}函数调用{{{java.lang.Thead.run()}}}方法,这个方法即是用户定义的新Java[[#note|*]]线程的线程体。
Line 456: Line 456:
||<:tablewidth="100%"> http://harmony.apache.org/subcomponents/drlvm/images/ThreadCreation.gif||
||<:>图 11. Java[#note *]线程的生命周期||
||<:tablewidth="100%"> {{http://harmony.apache.org/subcomponents/drlvm/images/ThreadCreation.gif}}||
||<:>图 11. Java[[#note|*]]线程的生命周期||
Line 463: Line 463:
[[Anchor(线程挂起)]] <<Anchor(线程挂起)>>
Line 481: Line 481:
一个典型的安全挂起场景的例子发生在垃圾收集器挂起一个Java[#note *]线程以便进行活引用枚举的时候。图 12 展示了GC使用线程管理器挂起运行在安全区的Java[#note *]线程的情况。

||<:tablewidth="100%">http://harmony.apache.org/subcomponents/drlvm/images/safeRegion.gif||
一个典型的安全挂起场景的例子发生在垃圾收集器挂起一个Java[[#note|*]]线程以便进行活引用枚举的时候。图 12 展示了GC使用线程管理器挂起运行在安全区的Java[[#note|*]]线程的情况。

||<:tablewidth="100%">{{http://harmony.apache.org/subcomponents/drlvm/images/safeRegion.gif}}||
Line 488: Line 488:
另一个安全挂起情况是当一个GC线程碰到一个运行在非安全代码区的Java[#note *]线程,如图13所示。

||<:tablewidth="100%">http://harmony.apache.org/subcomponents/drlvm/images/safePoint.gif||
另一个安全挂起情况是当一个GC线程碰到一个运行在非安全代码区的Java[[#note|*]]线程,如图13所示。

||<:tablewidth="100%">{{http://harmony.apache.org/subcomponents/drlvm/images/safePoint.gif}}||
Line 495: Line 495:
[[Anchor(Stop_the_world_线程挂起)]] <<Anchor(Stop_the_world_线程挂起)>>
Line 498: Line 498:
当垃圾收集器需要为某个线程组中所有线程枚举活对象引用时,将发生stop-the-world线程挂起。图 14 展示了当只有一个GC线程但是有无数Java[#note *]线程在运行时的情况,此时GC需要挂起所有Java[#note *]线程。

||<:tablewidth="100%">http://harmony.apache.org/subcomponents/drlvm/images/SuspendAll.gif||
当垃圾收集器需要为某个线程组中所有线程枚举活对象引用时,将发生stop-the-world线程挂起。图 14 展示了当只有一个GC线程但是有无数Java[[#note|*]]线程在运行时的情况,此时GC需要挂起所有Java[[#note|*]]线程。

||<:tablewidth="100%">{{http://harmony.apache.org/subcomponents/drlvm/images/SuspendAll.gif}}||
Line 503: Line 503:
首先,垃圾收集器调用线程管理器接口函数{{{hythread_suspend_all()}}}以便挂起运行在给定线程组中的每一线程(在本示例中,所有Java[#note *]线程)。线程管理器随后返回用于遍历被挂起线程列表的迭代器。GC使用该迭代器分析每一Java[#note *]线程与活引用的关系,然后进行垃圾回收。完成后,GC通知线程管理器恢复所有线程。

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

<<Anchor(线程锁定)>>
Line 510: Line 510:
||<:tablewidth="100%">http://harmony.apache.org/subcomponents/drlvm/images/locking.gif|| ||<:tablewidth="100%">{{http://harmony.apache.org/subcomponents/drlvm/images/locking.gif}}||
Line 517: Line 517:
[[Anchor(监视器的进入和退出)]] <<Anchor(监视器的进入和退出)>>
Line 520: Line 520:
锁定Java[#note *]监视器意味着在线程管理器和VM core之间的交互,因为线程管理器需要请求Java[#note *]对象中的内存地址,其中保存锁数据。锁定Java[#note *]监视器的过程如图 16所示。

如果Java[#note *]代码中有同步片段,会有以下步骤:
锁定Java[[#note|*]]监视器意味着在线程管理器和VM core之间的交互,因为线程管理器需要请求Java[[#note|*]]对象中的内存地址,其中保存锁数据。锁定Java[[#note|*]]监视器的过程如图 16所示。

如果Java[[#note|*]]代码中有同步片段,会有以下步骤:
Line 525: Line 525:
 1. {{{hythread_monitor_enter()}}}帮助器调用VM core组件的{{{vm_object_get_lockword_addr()}}}函数来查找Java[#note *]对象中锁字的物理地址。  1. {{{hythread_monitor_enter()}}}帮助器调用VM core组件的{{{vm_object_get_lockword_addr()}}}函数来查找Java[[#note|*]]对象中锁字的物理地址。
Line 527: Line 527:
 1. 一旦锁被获取,则帮助器返回。这个获取Java[#note *]监视器的快速路径。在这个场景下,帮助器不需要在Java[#note *]和native帧之间切换,{{{thin_monitor_try_enter()}}}会被直接调用,不涉及任何Java[#note *]对象。否则,帮助器代码通过在Java[#note *]和native代码之间切换而进入一个慢速路径(参见下图中压入一个{{{M2nFrame}}}并创建本地句柄的动作)。
 1. 帮助器调用{{{jthread_monitor_enter()}}}函数,该函数就像在JNI代码一样与Java[#note *]对象协同工作。
 
||<:tablewidth="100%">http://harmony.apache.org/subcomponents/drlvm/images/Monitors.gif||
||<:>图 16. 锁定Java[#note *]监视器||

[[Anchor(参考文献)]]
 1. 一旦锁被获取,则帮助器返回。这个获取Java[[#note|*]]监视器的快速路径。在这个场景下,帮助器不需要在Java[[#note|*]]和native帧之间切换,{{{thin_monitor_try_enter()}}}会被直接调用,不涉及任何Java[[#note|*]]对象。否则,帮助器代码通过在Java[[#note|*]]和native代码之间切换而进入一个慢速路径(参见下图中压入一个{{{M2nFrame}}}并创建本地句柄的动作)。
 1. 帮助器调用{{{jthread_monitor_enter()}}}函数,该函数就像在JNI代码一样与Java[[#note|*]]对象协同工作。
 
||<:tablewidth="100%">{{http://harmony.apache.org/subcomponents/drlvm/images/Monitors.gif}}||
||<:>图 16. 锁定Java[[#note|*]]监视器||

<<Anchor(参考文献)>>
Line 536: Line 536:
[1] J2SE 1.5.0 specification, http://java.sun.com/j2se/1.5.0/docs/api/ [[Anchor(ref1)]]

[2] JVM Tool Interface Specification, http://java.sun.com/j2se/1.5.0/docs/guide/jvmti/jvmti.html [[Anchor(ref2)]]

[3] Java[#note *] Native Interface Specification, http://java.sun.com/j2se/1.5.0/docs/guide/jni/spec/jniTOC.html [[Anchor(ref3)]]

[4] Apache Portable Runtime project, http://apr.apache.org/ [[Anchor(ref4)]]

[5] POSIX standard in threading, http://www.opengroup.org/onlinepubs/009695399/idx/threads.html [[Anchor(ref5)]]

[6] David F. Bacon, Ravi Konuru, Chet Murthy, Mauricio Serrano, Thin locks: featherweight synchronization for Java, http://portal.acm.org/citation.cfm?id=277734 [[Anchor(ref6)]]

[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 [[Anchor(ref7)]]

[8] HyThread documentation, http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/doc/vm_doc/html/group__Thread.html [[Anchor(ref8)]]

* Other brands and names are the property of their respective owners. [[Anchor(note)]]
[1] J2SE 1.5.0 specification, http://java.sun.com/j2se/1.5.0/docs/api/ <<Anchor(ref1)>>

[2] JVM Tool Interface Specification, http://java.sun.com/j2se/1.5.0/docs/guide/jvmti/jvmti.html <<Anchor(ref2)>>

[3] Java[[#note|*]] Native Interface Specification, http://java.sun.com/j2se/1.5.0/docs/guide/jni/spec/jniTOC.html <<Anchor(ref3)>>

[4] Apache Portable Runtime project, http://apr.apache.org/ <<Anchor(ref4)>>

[5] POSIX standard in threading, http://www.opengroup.org/onlinepubs/009695399/idx/threads.html <<Anchor(ref5)>>

[6] David F. Bacon, Ravi Konuru, Chet Murthy, Mauricio Serrano, Thin locks: featherweight synchronization for Java, http://portal.acm.org/citation.cfm?id=277734 <<Anchor(ref6)>>

[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 <<Anchor(ref7)>>

[8] HyThread documentation, http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/doc/vm_doc/html/group__Thread.html <<Anchor(ref8)>>

* Other brands and names are the property of their respective owners. <<Anchor(note)>>

线程管理

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

1. 修订历史

2. 关于本文档

3. 概览

4. 体系结构

5. 使用场景

6. 参考文献

1. 修订历史

版本

版本信息

日期

初版

Nadya Morozova, Andrey Chernyshev: 创建文档.

2006年6月5日

2. 关于本文档

2.1. 目的

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

2.2. 面向的读者

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

2.3. 文档约定

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

2.4. 文档使用

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

  • 概览:给线程管理器组件及其在VM体系结构中的地位做了一个概括的定义。

  • 体系结构:描述了线程管理器的内部结构、数据结构和开放的接口。

  • 使用场景:展示了主要的线程相关操作,例如线程生命周期和线程挂起。

  • 参考文献:连接到与本文内容有关的材料。

3. 概览

线程管理器(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所示。这些接口被组织在一个头文件集合中,在下面的对外接口一节中描述。

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

图 1. 线程子系统

3.1. 主要特点

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

  • 支持J2SE API1]、JVMTI2]和JNI3]规范要求的线程功能

  • 基于Apache Porting Layer的可移植实现4]

  • 遵从Harmony hythread 接口8]

  • 支持垃圾收集器
  • 特定于DRLVM提供的即时编译器(just-in-time, JIT)的监视器(monitor)优化

3.2. 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体系结构中的线程管理器

3.3. 可移植性

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

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

4. 体系结构

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

4.1. 对外接口

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

4.1.1. Native接口

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

Native接口包含下列函数集:

hythread.h


hythread集合的函数8],其作用包括:

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

hythread_ext.h


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

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

4.1.2. Java* 接口

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

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

jthread.h


支持java.lang.Objectjava.lang.Thread API的函数,其作用包括:

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

ti_thread.h


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

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

thread_helpers.h


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

4.2. 数据结构

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

4.3. 线程控制结构

线程管理器要求每个线程在调用线程支持函数前要先被注册(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组件使用操作那些结构的不透明句柄进行工作,而不能获得它们的内容信息。以这种方式与线程工作的时候,组件调用系附函数中的一个,获得该线程的线程控制结构的不透明句柄,然后就可以使用这个句柄对这个线城进行任何操作了。

4.3.1. Native 线程结构

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

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

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

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

图 3. 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头文件。

4.3.2. Java* 线程结构

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

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

图 4. Java* 系附的线程

下表列出了组成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中展示。

4.4. 线程组

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

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

图 5. 线程组

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

4.5. 同步器

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

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

图 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同步器的不同集合。

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

4.6. 监视器

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

4.6.1. 膨胀技术

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

  • 在没有线程竞争的情况下,锁数据被存储在若干字节当中,从而可以在分配Java*对象的同时直接分配锁。

  • 无论竞争何时发生,为锁数据分配的若干字节保存一个对fat锁的引用,这个fat锁可以是一个传统的互斥量。

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

4.6.2. 监视器结构

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

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

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

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

图 7. 锁字结构:Contention Bit为0

  • Contention bit:0表示没有竞争;
  • Thread ID (15 bits): 持有该锁的线程ID。如果锁未被任何线程持有,则该位为0;
  • Recursion count: 锁被同一个线程反复申请的次数减1;
  • Reservation bit: 指示锁是否被一个线程保留的标志7];

  • 最右边的10 bits在TM中未使用,它们被保留用作存储Java*对象的哈希码。

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

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

图 8. 锁字结构:Contention Bit为1

  • Contention bit: 1表示存在竞争;
  • Fat Lock ID (20 bits): 相应fat锁的ID;
  • Reservation bit: 指示锁是否被一个线程保留的标志7];

  • 最右边的10 bits在TM中未使用,它们被保留用作存储Java*对象的哈希码。

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

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

图 9. Thin和Fat监视器的关系

4.6.3. 获取一个监视器

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

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

图 10. 申请一个Thin锁

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

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

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

5. 使用场景

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

5.1. Java* 线程生命期

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

  1. 在创建了一个线程之后,Thread()构造函数创建一个新的native线程并初始化线程控制结构HyThreadJVMTIThread

  2. 用户应用程序然后调用VM core组件中内核类的java.lang.Thread.start()方法。

  3. java.lang.Thread.start()方法通过java.lang.VMThreadManager.start()函数向线程管理器中的Java* layer的jthread_create()函数委托一个调用。

  4. jthread_create()函数调用jthread_create_with_function()

  5. jthread_create_with_function()函数调用hythread_create()函数,提供wrapper_proc作为一个新线程体的过程。

  6. hythread_create()函数向APR porting layer的apr_thread_create()函数委托一个调用,它本身并不真正fork并创建一个新线程。在这一步,提供{{{wrapper_proc参数作为线程体。

  7. 新创建的线程开始执行thread_start_proc(),这个函数再转而执行wrapper_proc()函数。

  8. 函数wrapper_proc()执行新线程向其它组件的注册请求。方法是通过向VM core发送vm_attach事件,并调用jvmti_send_thread_start_end_event()函数,它会发送JVMTI_EVENT_THREAD_START事件。

  9. wrapper_proc()函数调用java.lang.Thead.run()方法,这个方法即是用户定义的新Java*线程的线程体。

  10. Thread.run()结束之后,线程向其它组件注销。方法是调用jvmti_send_thread_start_end_event()函数会,它会先发送JVMTI_EVENT_THREAD_END事件,然后是vm_detach事件。

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

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

图 11. Java*线程的生命周期

  • 注意:

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

5.2. 线程挂起

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)函数执行下面机制中的一个:

    1. 如果线程T2当前运行在安全代码区,hythread_suspend(T2)调用立即返回,如图 12。

    2. 如果线程T2当前运行在非安全代码区,hythread_suspend()在T2到达安全区开始点或者安全点之前都会阻塞。

  2. 线程T2运行到安全区结束点,然后阻塞直到T1调用hythread_resume(T2)恢复它。

线程T2经历如下:

  1. 线程T2周期性地调用hythread_safe_point()函数指定安全挂起点。如果之前有对T2的挂起请求,该方法通知T1然后等待直到T1调用hythread_resume(T2)恢复T2。

  2. 当T2进入安全区,它调用hythread_suspend_ensable()方法,该方法减少suspend_disable_count状态标志。如果之前有对T2的挂起请求,T1会得到T2已经到达安全区的通知。

  3. 当T2离开安全区,它调用hythread_suspend_disable()函数,该函数增加suspend_disable_count状态标志。

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

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

图 12. 挂起:安全区

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

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

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

图 13. 安全点

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

5.3. Stop-the-world 线程挂起

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

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

图 14. 挂起一个线程组

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

5.4. 线程锁定

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

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

图 15. 用互斥量锁定

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

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

5.5. 监视器的进入和退出

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

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

  1. JIT生成的代码调用线程管理器提供的hythread_monitor_enter()帮助器函数。帮助器函数提供一个代码块(存根),该存根可被JIT直接内联到所生成的汇编码中。

  2. hythread_monitor_enter()帮助器调用VM core组件的vm_object_get_lockword_addr()函数来查找Java*对象中锁字的物理地址。

  3. 帮助器调用thin_monitor_try_lock()函数以便获取对象关联的锁。

  4. 一旦锁被获取,则帮助器返回。这个获取Java*监视器的快速路径。在这个场景下,帮助器不需要在Java*和native帧之间切换,thin_monitor_try_enter()会被直接调用,不涉及任何Java*对象。否则,帮助器代码通过在Java*和native代码之间切换而进入一个慢速路径(参见下图中压入一个M2nFrame并创建本地句柄的动作)。

  5. 帮助器调用jthread_monitor_enter()函数,该函数就像在JNI代码一样与Java*对象协同工作。

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

图 16. 锁定Java*监视器

6. 参考文献

[1] J2SE 1.5.0 specification, http://java.sun.com/j2se/1.5.0/docs/api/

[2] JVM Tool Interface Specification, http://java.sun.com/j2se/1.5.0/docs/guide/jvmti/jvmti.html

[3] Java* Native Interface Specification, http://java.sun.com/j2se/1.5.0/docs/guide/jni/spec/jniTOC.html

[4] Apache Portable Runtime project, http://apr.apache.org/

[5] POSIX standard in threading, http://www.opengroup.org/onlinepubs/009695399/idx/threads.html

[6] David F. Bacon, Ravi Konuru, Chet Murthy, Mauricio Serrano, Thin locks: featherweight synchronization for Java, http://portal.acm.org/citation.cfm?id=277734

[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

[8] HyThread documentation, http://svn.apache.org/viewvc/incubator/harmony/enhanced/classlib/trunk/doc/vm_doc/html/group__Thread.html

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

线程管理 (last edited 2009-09-20 21:54:51 by localhost)