JVM——线程通信原理
2026/6/3 18:30:18 网站建设 项目流程

JVM线程通信原理

    • 1、ParkEvent原理
      • 1.1、Allocate方法
      • 1.2、Rlease方法
      • 1.3、park方法
      • 1.4、unpark方法
    • 2、Parker实现原理
      • 2.1、park方法
      • 2.2、unpark方法
    • 3、sleep方法实现原理
      • 3.1、JVM_Sleep函数
      • 3.2、sleep函数
    • 4、ObjectMonitor实现原理
      • 4.1、数据结构
      • 4.2、ObjectMonitor的构造函数
      • 4.3、ObjectWaiter源代码
      • 4.4、TryLock方法
      • 4.5、EnterI方法
      • 4.6、ReenterI方法
      • 4.7、enter方法
      • 4.8、exit方法
      • 4.9、ExitEpilog方法
    • 5、wait与notify方法实现原理
      • 5.1、设计原理
      • 5.2、wait方法实现原理
      • 5.3、notify方法实现原理
      • 5.4、notifyAll方法实现原理
    • 6、yield方法实现原理
    • 7、 join方法实现原理
      • 7.1、JVM_IsThreadAlive
      • 7.2、ensure_join
    • 8、stop方法实现原理
      • 8.1、JVM_StopThread
      • 8.2、HAS_PENDING_EXCEPTION
    • 9、interrupt方法实现原理

线程在系统中是独立运行的,但是许多业务场景都需要多线程去协同工作,所以Java在Thread类与Object类里提供了大量的方法来实现线程间的通信。本章会详细讲解sleep、wait、notify、join、stop等通信机制的设计原理与JVM实现过程。

1、ParkEvent原理

在JVM中,ParkEvent是核心类,它可以实现线程的阻塞与唤醒。ParkEvent提供的park方法可以阻塞线程,unpark方法可以唤醒线程。Java的synchronized与wait方法都是通过ParkEvent来实现线程的阻塞与唤醒的。ParkEvent与线程的生命周期相关联。当线程创建时,会从EventFreeList查找一个空闲的ParkEvent,绑定到线程上。线程执行结束时,将ParkEvent归还到EventFreeList链表中。EventFreeList相当于一个ParkEvent的对象池,和常见的数据库连接池相似。

在Linux操作系统中,ParkEvent是基于POSIX协议的PlatformEvent实现的,核心方法如表所示。

1.1、Allocate方法

Allocate方法用来获取一个ParkEvent实例对象,具体实现如代码清单所示。该方法的实现思路是,先尝试从FreeList中获取一个空闲的ParkEvent,如果有空闲的ParkEvent就直接使用;如果没有空闲的ParkEvent,就创建一个ParkEvent对象。

FreeList是一个全局的对象,表示当前空闲的ParkEvent的开始位置。FreeNext是一个指针链表,维护的是所有空闲的ParkEvent。Allocate每次都会尝试获取FreeList,如果FreeList不为空,就从FreeList的链表里获取一个空闲的ParkEvent。如果不存在空闲的ParkEvent,就重新构建一个ParkEvent。ParkEvent对象池租用的流程如图4-1所示。

1.2、Rlease方法

每个线程执行完任务,都要在销毁线程时调用Release方法,以将ParkEvent归还到对象池中。代码清单是Release方法的具体实现。

Release方法会将当前线程的ParkEvent设置成空闲的状态,然后将当前线程的ParkEvent加入FreeList中。ParkEvent归还流程如图所示。

1.3、park方法

park方法的处理流程如下:

  1. 先通过CAS的方式将_event减1,来确保当前的ParkEvent只能被一个线程调用。
  2. 通过millis_to_nanos_bounded函数把传入的毫秒时间转换成纳秒时间,计算出需要挂起的具体阻塞到期时间。
  3. 调用pthread_mutex_lock函数来获取对象锁,然后调用pthread_cond_timedwait函数将当前线程阻塞。当阻塞时间到了之后,系统内核会把当前线程恢复成运行状态。
  4. 调用pthread_mutex_unlock函数来释放对象信息,然后调用OrderAccess::fence()函数来使CPU的本地缓存失效。

    整个过程中有两个很重要的点:一是会通过CAS来确保同一个ParkEvent不能同时执行多次park操作;二是在等待结束之后,当前线程会被系统重新调度运行,在运行之前会调用OrderAccess::fence()来刷新CPU的缓存,这样就避免了上下文切换带来的内存数据可见性。

1.4、unpark方法

unpark方法的核心功能是唤醒等待的线程,具体实现如代码所示。unpark方法是通过pthread_cond_signal函数传递信号来唤醒已经阻塞的线程的。unpark方法的核心流程如下:首先判断线程是否挂起,当_event的值小于1,表示当前线程被阻塞,只有被阻塞的线程才能进行唤醒。其次,判断线程阻塞的次数,只有表示阻塞次数的_nParked值大于0的时候才能唤醒线程。最后,调用系统函数pthread_cond_signal来发送线程唤醒信号。

Java里的synchronized关键字与Object.wait()方法都是通过ParkEvent来实现线程阻塞与唤醒的。

2、Parker实现原理

Parker和ParkEvent的功能一样,都是用来进行线程阻塞与唤醒的。Parker主要是为JDK里面的Unsafe类服务的,以工具类的方式对线程进行阻塞与唤醒。Parker类通过_counter来判断当前线程状态:0表示可以阻塞,1表示进行唤醒。

Parker主要有park与unpark两个方法,详细信息如表所示。

2.1、park方法

park方法有两个参数:isAbsolute以及表示时间值的time。当isAbsolute为true的时候,time需要传递的是未来的某个具体时间值。当isAbsolute为false的时候,time需要传递时间值,也就是从当前时间开始多少毫秒后线程会被唤醒。


park方法的核心处理流程如下。

  1. 判断传入的时间参数是否大于0:如果小于0则直接返回;如果大于0,则将time转换成具体的时间值。将当前线程状态设置成阻塞状态。
  2. 判断_counter是否大于0:如果_counter大于0,说明已经调用过unpark方法了,直接返回。然后刷新CPU本地缓存,让CPU缓存中的数据与内存同步,这样在线程阻塞后其他线程就能看到当前线程对数据的修改。
  3. 调用pthread_cond_wait或pthread_cond_timedwait函数将当前线程阻塞。当阻塞时间到了,线程醒来后会再次让本地CPU的缓存失效,让当前线程能够看到其他线程对共享数据的修改。

2.2、unpark方法

unpark方法是用来唤醒被阻塞的线程的,它首先改变_counter的状态,防止其他线程同时调用park方法,然后判断如果其他线程正处于park状态就执行unpark操作。unpark方法具体实现如代码清单所示。

核心实现的逻辑就是调用pthread_cond_signal函数来发送信号,唤醒正在等待的线程。

3、sleep方法实现原理

sleep方法会让当前线程休眠,millis参数是指要睡眠的时间值(ms),当millis为0时表示永久睡眠。sleep方法描述如代码所示。

3.1、JVM_Sleep函数

在JVM中,sleep方法对应的JNI是JVM_Sleep函数。JVM_Sleep函数的核心处理逻辑如下。

  1. 判断要睡眠的时间是否正确:如果millis小于0则直接报错,millis等于0表示是永远沉睡,millis大于0表示设定的沉睡的时间。
  2. 将JavaThread线程状态设置成SLEEPING状态,同时把内核线程对象OsThread设置成SLEEPING状态。如果时间值是0,调用os:naked_yield函数直接放弃任务执行。如果时间值大于0,则调用JavaThread的sleep函数让当前线程睡眠。在线程醒来后,把OsThread恢复到睡眠前的状态。

JVM_Sleep函数具体实现如代码所示。

3.2、sleep函数

JavaThread的sleep函数的主要功能是调用系统函数让当前线程睡眠,同时在线程睡眠前刷新CPU本地缓存,并在醒来后再次刷新CPU本地缓存。代码是sleep函数的具体实现,核心逻辑如下。

  1. 调用OrderAccess::fence()函数刷新CPU本地缓存,强制完成CPU本地缓存中修改的数据与主内存同步,确保当前线程修改的数据对其他线程可见。判断线程是否已经发生了中断,如果发生了中断就结束当前线程的睡眠状态。
  2. 调用ParkEvent的park方法来将当前线程睡眠。
  3. 睡眠结束后,会接着判断是否按要求达到了睡眠时间,如果没有达到就通过for循环继续睡眠。

线程睡眠过程中有几个重要的知识点需要大家重点关注,详细信息如表所示。

4、ObjectMonitor实现原理

Java语言的优势之一是它在语言级别支持多线程,能够很好地协调多个线程之间的互动和数据访问。ObjectMonitor是Java同步机制的具体实现。ObjectMonitor支持2种线程同步:互斥和协作。ObjectMonitor通过支持对象锁互斥,使多个线程能够独立处理共享数据,而不会相互干扰,通过支持Object的wait和notify方法,使线程能够朝着一个共同目标一起工作。ObjectMonitor是synchronized关键字与wait、nofity方法的底层实现。

ObjectMonitor提供了监视器锁获取与释放的相关方法,详细信息如表所示。

4.1、数据结构

ObjectMonitor方法的数据结构从整体上可以分成两个部分:一部分监视器锁的实时状态,另一部分监视器锁的排队信息,数据结构如图所示。

ObjectMonitor方法中的字段的详细信息如表所示。

4.2、ObjectMonitor的构造函数

在ObjectMonitor的构造函数中对owner、object、cxq等字段进行初始化,具体实现如代码所示。

4.3、ObjectWaiter源代码

cxq、waitSet、entryList等待队列中的元素是ObjectWaiter。ObjectWaiter用来表示没有获取到线程锁对象而在等待队列中的线程。ObjectWaiter的实现如代码所示。


ObjectWaiter采用的是双向链表的数据结构,_prev指向前驱节点、_next指向后继节点。每次往等待队列中添加与删除元素都只修改_prev与_next指针即可。

4.4、TryLock方法

TryLock方法首先判断当前ObjectMonitor监视器对象是否已经被线程持有,如果对象是空闲的,则调用try_set_owner_from方法将当前线程设置为监视器对象的持有者,具体实现如代码所示。

try_set_owner_from方法(代码如下)是通过CAS的方式来判断_owner字段是否为空,如果owner为空,则将当前线程ID设置到_owner字段中。

4.5、EnterI方法

EnterI方法是线程进入等待队列的核心实现。EnterI方法会先通过自旋的方式获取锁,如果获取锁失败,再通过排队的方式获取锁。代码如下是EnterI方法的具体实现,核心流程如下。

  1. 依次调用TryLock方法、try_set_owner_from方法、TrySpin方法来尝试快速获取监视器锁,如果获取锁成功,则直接返回。
  2. 如果无法快速获取监视器锁,就构造线程等待对象ObjectWaiter,并将当前线程插入到_cxq队列的尾部进行等待。
  3. 在加入_cxq等待队列后,调用ParkEvent的park方法将当前线程阻塞。
  4. 当其他线程释放监视器锁的时候,会尝试唤醒当前线程来接着获取监视器锁。如果当前线程获取到了监视器对象,会调用OrderAccess的fence方法刷新当前CPU的缓存,使得当前线程能够看到其他线程对数据的修改以及对锁状态的修改。
  5. 调用UnlinkAfterAcquire方法将当前线程从_cxq队列中移除。

4.6、ReenterI方法

ReenterI方法的核心功能是获取监视器锁对象,该方法是专门为因使用wait方法而等待的线程再次获取锁而设计的。下面代码是ReenterI方法的具体实现,核心流程如下。

  1. 尝试通过TryLock方法来获取监视器锁,如果获取不到再调用TrySpin方法来获取监视器锁。
  2. 如果无法获取到监视器锁,就调用ParkEvent的park方法将当前线程阻塞。
  3. 当其他线程释放了监视器锁之后,会通知当前线程来获取锁。当前线程会再次调用TryLock方法获取监视器锁,如果获取成功就调用OrderAccess的fence方法刷新缓存。

4.7、enter方法

enter方法是获取监视器锁的入口方法,核心思想是尽一切可能获取锁,如果实在无法获取到锁,会让当前线程加入等待队列进行等待。同时enter方法支持重入锁的功能,如果线程已经获取到锁了,再次调用enter方法就直接返回成功标志。

如下代码是enter方法的具体实现,核心流程如下。

  1. 首先尝试通过try_set_owner_from方法快速获取锁,如果获取到,则直接返回成功标志。
  2. 判断当前线程是否拿到锁,如果已经拿到锁了,则直接返回成功标志。
  3. 如果当前线程没拿到锁,则接着调用TrySpin方法快速获取监视器锁。
  4. 如果无法快速获取到监视器锁,则最终会调用EnterI方法通过队列排队的方式来获取监视器锁。

4.8、exit方法

exit方法有两个核心功能:一是释放当前线程持有的监视器锁;二是唤醒其他线程来获取监视器锁。代码是exit方法的具体实现,核心流程如下。

  1. 判断当前线程是否持有监视器锁,如果未持有监视器锁,则直接返回。
  2. 调用OrderAccess的storeload方法刷新CPU本地缓存,确保当前线程修改的数据对其他线程可见。
  3. 判断_EntryList队列是否为空,如果不为空就调用ExitEpilog方法从_EntryList队列中获取一个等待线程,通知该线程来获取监视器锁。
  4. 如果_EntryList队列为空,接着判断_cxq队列是否为空,如果_cxq不为空,则将_cxq队列中所有元素迁移到_EntryList队列中。
  5. 最后再唤醒_EntryList队列头部的线程来获取监视器锁。


4.9、ExitEpilog方法

ExitEpilog方法有两个功能:一是释放当前线程持有的监视器锁,另一个是唤醒等待中的线程来获取监视器对象。如下代码是ExitEpilog方法的具体实现,核心的流程如下。

  1. 调用release_clear_owner方法释放掉当前线程锁持有的监视器锁。
  2. 调用OrderAccess的fence方法刷新CPU本地缓存,让其他线程感知到监视器锁已经空闲。
  3. 获取等待节点的ParkEvent,调用ParkEvent的unpark方法唤醒等待的线程。

5、wait与notify方法实现原理

在Java并发编程中,可以通过调用Object的wait方法、notify方法和notifyAll方法来实现线程间同步。在程序中,可以调用wait方法将当前线程阻塞,释放CPU的资源,并等待其他线程的通知。其他线程可以调用notify方法或notifyAll方法唤醒处于等待状态的线程。

如下代码是一个通过wait方法、notify方法来实现线程间同步的示例。

WaitTask的功能是实现线程等待:在线程等待之前打印了开始进行等待的信息;在线程等待结束以后,打印了结束等待的信息。NotifyTask的功能是唤醒等待的线程,在通知线程唤醒之后打印通知唤醒信息。WaitTest是测试类,其内部定义的String类型的变量data用于同步对象,并启动了两个线程来执行WaitTask与NotifyTask。

5.1、设计原理

Java语言规定在使用Object的wait与notify方法时,必须用synchronized来同步代码块。在编译之后,synchronized关键字会变成monitorenter指令和monitorexit指令。在程序运行时,monitorenter指令会被解释成获取监视器锁,monitorexit指令会被解释成释放获取的监视器锁。wait方法内部有4个核心逻辑:将当前线程加入等待队列、释放监视器锁、将当前线程阻塞、重新获取监视锁。整个wait方法的核心流程如下。

  1. 进入同步代码块,获取监视器锁,只有获取了监视器锁才能调用wait方法。
  2. 将当前线程加入等待队列进行等待。
  3. 释放监视器锁,当前线程不再持有对象的监视器锁,这样监视器锁就能被其他线程获取。
  4. 将当前线程阻塞,等待其他线程唤醒。
  5. 当前线程被唤醒后重新获取对象锁。

wait与notify方法的交互逻辑如图所示。

Object类里面定义了wait、notify、notifyAll等方法来协调线程的执行顺序,方法定义如代码所示。

notifyAll并不能唤醒所有等待的线程,而是尝试通知所有等待的线程,最终只有一个线程能被唤醒。

5.2、wait方法实现原理

Object的wait方法在JVM里面的对应实现是ObjectMonitor的wait方法。ObjectMonitor的wait方法会先释放掉ObjectMonitor对象锁,然后将等待节点加入ObjectMonitor的等待队列,并将当前线程阻塞。当其他线程调用notify或notifyAll方法唤醒当前线程后,当前线程会重新获取ObjectMonitor监视器锁,拿到锁之后继续执行业务逻辑。ObjectMonitor的wait方法的具体实现如代码所示,其核心逻辑如下。

  1. 检测当前线程是否持有监视器锁,如果当前线程未持有监视器锁,则直接报错。
  2. 利用当前线程构建ObjectWaiter等待节点,并将等待节点设置为TS_WAIT(等待)状态。
  3. 调用OrderAccess的fence方法刷新CPU本地缓存,确保线程修改的数据对其他线程实时可见。
  4. 通过自旋锁进行并发控制,将当前线程加入等待队列进行等待。
  5. 调用ObjectMonitor的exit方法释放监视器锁,这样监视器锁可以被其他线程获取。
  6. 调用ParkEvent的park方法将当前线程阻塞,当前线程进入阻塞状态,需要其他线程通知才能唤醒。
  7. 当前线程被其他线程唤醒之后,首先检测是否从TS_WAIT状态唤醒,接着把当前节点从等待队列中移除。
  8. 调用OrderAccess的loadload方法重新加载数据,确保当前线程能获取到最新数据。
  9. 调用enter方法再次获取监视器锁,获取到锁之后继续执行线程任务。



5.3、notify方法实现原理

ObjectMonitor的notify方法是Object的notify方法的具体实现。ObjectMonitor的notify方法的核心功能是唤醒被wait方法阻塞的线程。如下代码是notify方法的具体实现,它首先要确认当前线程是否持有监视器锁,如果当前线程持有监视器锁就调用INotify方法来唤醒队列中等待的线程。如果当前线程未持有监视器锁,则直接抛出异常。

INotify方法是notify的底层实现,核心任务是管理等待队列。如代码清单4-23所示,INotify方法会获取WaitSet队列的头节点,并插入到_EntryList等待队列中。

5.4、notifyAll方法实现原理

Object的notifyAll方法在JVM里面的实现是ObjectMonitor的notifyAll方法。Object-Monitor的notifyAll方法的核心功能是管理线程等待队列,确保调用wait方法的等待线程能被优先唤醒。如下代码是notifyAll方法的具体实现。

wait方法与notify方法都是通过ObjectMonitor来实现的。wait方法会先将当前线程加入ObjectMonitor的_WaitSet等待队列中,然后调用ParkEvent的park方法让当前线程阻塞,当前线程正式进入等待。notify方法只是将_WaitSet队列中的头节点移动到_EntryList中,不会直接唤醒线程。当synchronized退出的时候,会调用ObjectMonitor的exit方法将_EntryList中的等待节点唤醒。notifyAll方法是将_WaitSet队列中所有的节点都迁移到_EntryList队列。

6、yield方法实现原理

Thread类的yield方法的功能是让当前线程放弃执行,让出CPU资源,让其他线程获得运行机会。操作系统会从可执行队列里选择一个线程任务来执行,也可能是刚刚让出CPU的当前线程。

yield方法是一个全局静态的本地方法,该方法能够让当前线程放弃在CPU的执行,方法定义如代码所示。


yield方法对应的JNI的函数是JVM_Yield。JVM_Yield函数的源码如代码所示。

JVM_Yield的功能是依靠操作系统来实现的。如果系统不支持主动放弃任务调度,JVM_Yield函数什么也不干,直接返回。如果系统支持主动让出任务调度,则JVM_Yield函数会调用os::naked_yield函数来放弃当前任务调度。

os::naked_yield函数在不同的操作系统上有不同的实现。例如在Linux操作系统中,是通过sched_yield(系统内核函数)来实现的,具体如代码所示。

sched_yield主要功能是将当前线程任务从CPU任务队列中移除,重新触发系统内核对其他可执行状态的任务的调度。

7、 join方法实现原理

Java的线程采用的是线程分离策略,即主线程不会等待子线程运行结束,主线程自己运行结束了就终止了。但在特定的业务场景中,主线程需要等子线程执行结束后继续处理业务,所以Thread类提供了join方法来实现主/子线程间同步。

Thread类提供了2种join方法:一种是永久等待,父线程会一直等待子线程运行结束;另一种是超时等待,如果等待时间到了,子线程还没有结束,则主线程也会继续执行。join方法的源码如代码所示。

join方法的逻辑如下。

  1. 判断millis是否小于0,如果millis小于0则抛出异常。
  2. 调用isAlive方法判断子线程是否存活,只有存活的线程才能等待。
  3. 调用wait方法将当前线程阻塞。

isAlive是用final修饰的本地方法,用final修饰就是为了防止子类重写该方法,因为线程存活判断需要调用JVM接口函数。isAlive方法定义如代码所示。

7.1、JVM_IsThreadAlive

isAlive方法对应的JNI处理函数是JVM_IsThreadAlive。JVM_IsThreadAlive函数直接调用java_lang_Thread::is_alive函数来判断线程是否存活,具体源码如代码所示。

_eetop_offset的值就是JavaThread的对象信息,是在线程创建的时候调用set_thread全局函数设置进去的,在线程执行销毁的时候会将_eetop_offset设置为NULL。

7.2、ensure_join

线程执行完成之后会调用post_run方法来进行后置任务的处理。post_run方法会调用exit方法来处理线程的善后工作。exit方法最终会调用ensure_join来唤醒等待的线程。ensure_join方法主要负责修改线程的状态,将_eetop_offset的值设置为NULL,并通知等待的主线程。如下代码是ensure_join函数的具体实现。

ensure_join首先获取线程的锁对象ObjectLocker,然后把线程的状态修改为TERMIN-ATED,接着设置_eetop_offset的值为NULL,然后调用锁对象ObjectLocker的notify_all函数来通知主线程。

ObjectLocker的notify_all最终是通过ObjectSynchronizer的notify_all函数来实现的线程等待唤醒的。如下代码是ObjectLocker的notify_all方法的具体实现。

ObjectSynchronizer的notifyall在唤醒线程后,会主动刷新CPU本地缓存使得当前线程的修改的数据完成与主内存层同步。这样在join方法执行完成后,主线程就能看到子线程对数据的修改,这样就实现了happens-before原则中的子线程执行结束后主线程数据的可见性。

8、stop方法实现原理

Thread类提供的stop方法可以暴力停止线程。stop方法会解锁当前线程已锁定的所有监视器,并且强制停止业务执行逻辑,导致无法判断代码执行的正确性,所以一般不建议使用stop方法来停止线程。接下来详细讲解stop方法的实现原理。

stop方法是一个final方法,子类无法对其进行重写。stop方法会先判断是否有当前线程的停止权限,如果没有权限,则设置停止权限。stop方法的实现如代码所示。

首先构造一个ThreadDeath异常对象,然后调用stop0方法来停止线程。stop0是JVM的本地方法,对应的JNI函数是JVM_StopThread。

8.1、JVM_StopThread

JVM_StopThread的核心功能是模拟一个线程错误ThreadDeath,并将错误传递到线程中,阻止线程继续执行,达到线程暴力停止的效果。如下代码是JVM_StopThread函数的实现。它首先会判断当前线程是否存活,接着判断线程是否正在执行。如果线程处于执行状态,就调用THROW_OOP方法来直接设定异常,线程能够很快感知到异常信息,然后停止执行。如果线程处于等待状态,会调用send_async_exception函数来异步地设置异常信息。



THROW_OOP是_throw_oop方法的宏定义,具体实现如代码所示。_throw_oop方法实现的逻辑也比较简单,就是调用set_pending_exception来设置异常信息。

send_async_exception方法首先会构造一个throwable异常对象,然后构造一个异常处理器vm_stop,最后调用异常处理器来处理异常信息。

8.2、HAS_PENDING_EXCEPTION

在线程执行过程中,JVM会通过HAS_PENDING_EXCEPTION宏命令来判断线程是否发生异常,如果当前线程发生了异常就终止执行。HAS_PENDING_EXCEPTION宏命令对应的处理函数是has_pending_exception。HAS_PENDING_EXCEPTION实现异常检测的具体代码如代码所示。

在JVM执行每个方法或者代码块的时候都会调用HAS_PENDING_EXCEPTION来进行异常检查,如果有异常信息就直接退出。

stop方法的核心设计思想是模拟线程发生ThreadDeath异常,然后将异常信息设置到线程中。如果线程正在执行中,JVM发现线程有异常就结束执行。如果线程处于阻塞状态,JVM会先设置异常信息,然后触发线程中断来唤醒线程,线程被唤醒后会处理异常信息,结束执行。

9、interrupt方法实现原理

stop方法虽然能让线程暴力停止,但是带来的业务执行结果不可预期,现在已经不建议使用了。那有没有一种能让线程安全停止的方式呢?Thread类的interrupt方法可以实现。interrupt方法的核心思想是两阶段终止:第一阶段由主要线程发起终止命令,第二阶段由子线程来响应终止命令。这样就能提供两阶段提交,完成线程的优雅停止,中断的处理流程如图所示。

如下代码是一个简单例子,InterruptTest实现了一个简单的线程,线程的任务是循环打印线程执行的次数。在子线程启动完成后,主线程会调用interrupt方法向子线程发起中断请求,子线程在收到中断请求后就结束执行,这样就实现了线程的安全停止。

下面来了解interrupt方法的实现原理。interrupt方法会先判断线程是否阻塞在I/O操作上:如果阻塞在I/O操作上,会有底层的I/O处理器响应中断信息。不论是否处于I/O阻塞状态,都会先将线程中断的标记interrupted设置为true,最终都是调用interrupt0方法来实现线程中断通知。interrupt方法实现如代码所示。

interrupt0是一个本地方法,它对应的JNI函数是JVM_Interrupt。JVM_Interrupt函数首先会判断当前线程是不是存活的,如果活着就调用interrupt方法来发送中断信息,具体实现如代码所示。

JavaThread的interrupt方法有两个核心功能:一是设置线程中断状态,二是唤醒处于等待状态的线程。如果线程处于执行中的状态,则interrupt方法除了给线程打上中断标记之外,不会做任何处理。如果线程处于等待状态,则interrupt方法会唤醒线程。被唤醒的线程会清除中断标志,并抛出InterruptedException异常信息。代码是JavaThread的interrupt方法的具体实现。

具体对于中断的处理,需要开发人员在代码中做特定的业务处理。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询