中断实际上是一种“中断”事件,中断具体表明什么意思须要考虑它所处的上下文环境
和参照对象
是谁。考虑事件,咱们能够简单把中断抽象为这样一种模型:数组
当咱们分析某种中断事件时,咱们须要搞清楚这四个对象:异步
中断源ide
中断信号spa
中断控制器操作系统
好比说中断源发送的信号是否屏蔽,信号是否可被中断处理器重复处理,信号的处理是否有优先级...线程
中断处理器设计
在实际的中断事件中,并不必定恰好有上面提到的这四类对象,可能更复杂可能更简单化。可是当咱们考虑中断事件时,须要明确应该有相似功能的“对象”承担这样的逻辑。code
下面咱们主要围绕操做系统的中断机制
,Java的中断机制
,如何设计一个异步线程间的中断系统
这三部分简单探讨下。orm
与操做系统有关的中断,一般是指:程序在执行过程当中,遇到急需处理的事件时,暂时停止CPU上现行程序的运行, 转去执行相应的事件处理程序,待处理完成 后再返回原程序被中断处或调度其余程序执行的过程。对象
按照中断事件自己的不一样,能够划分为处理器以外的中断事件
,异常
,系统异常
。
指由外围设备发出的信号引发的,与当前运行指令无关的中断事件。示意图以下:
咱们分别以上述四个对象来看:
中断源:外部设备,如打印机,键盘,鼠标等。
触发条件:如外围设备报告I/O状态的I/O中断;外围设备发出的对应信号中断,如时钟中断,键盘/鼠标对应信号的中断,关机/重启动中断等。
触发方式:由外部设备向中断控制器发出中断请求IRQ。
也就是说中断源通知给中断控制器的是什么。
能够是经过一条信号线上产生特定的电平(利用高低电平表示是否中断两种状态),也能够在总线上发送特定消息或者消息序列,也能够是在中断寄存器中设置已发生的中断状态等。
CPU中的一个控制部件,包括 中断控制逻辑线路和中断寄存器。负责中断的发现和响应。
也就是说负责检查中断寄存器中的中断信号,当发现中断时让CPU切换当前进程程序,去处理中断程序。响应示意图以下:
指的是CPU接收到不一样的中断信号该怎么处理。包括“中断处理过程”和“恢复正常操做”两部分。
1.中断处理过程
首先CPU须要将当前运行进程的上下文保存,从中断进程中分析PSW,肯定对应的中断源和执行对应的中断处理程序。
小贴士
:PSW(Program Status Word): 是指在电脑中,一段包含被操做系统使用的程序状态信息的内存或硬件区域。通常用一个专门的寄存器来指示处理器状态。能够理解为咱们上面提到的中断信号存储装置.
2.恢复正常操做
当中断程序执行完毕,接下来执行哪一个进程由进程调度决定,由调度策略决定是否调度到中断执行前的进程。
较为完整的中断响应流程图以下:
异常 和 系统异常 这两类中断事件主要属于处理器执行特定的指令引发的中断事件。和上述硬件外围设备引发的中断事件的中断源不一样,中断的发起,控制和处理主要是由操做系统的指令逻辑和线路来承担。是一种同步的处理操做,而外部中断是由外部设备发起,是一种异步的处理操做。下面咱们简要介绍下。
异常指当前运行指令引发的中断事件。包括错误状况引发的故障,如除零算数错误,缺页异常;也包括不可恢复的致命错误致使的终止,一般是一些硬件错误。
对于故障
的处理,根据故障是否可以被恢复,故障处理程序要么从新执行引发故障的指令,要么终止。
对于终止
的处理,处理程序将控制返回给一个abort例程,该例程会终止这个应用程序。
系统异常指执行陷入指令而触发系统调用引发的中断事件,如请求设备、请求I/O、建立进程等。
这种有意的异常,称为陷阱处理。处理完成后陷阱程序会将控制返回给应用程序控制流的下一条指令。
总结一下,操做系统的中断类别行为以下:
好了,大头总算完了。由于小姐姐主要是Java码农,下面将主要介绍和Java相关的中断语义是什么。
理解了上面操做系统的中断以后,Java的中断机制就很easy了 :D
Java中断指的是 A线程发送中断信号给B线程,B线程再根据本身当前执行程序中的中断处理逻辑决定如何响应。嗯,就这么简单~
咱们来稍微分析一下中断事件中的“四个对象”:
中断源:A线程
中断触发条件:A线程说了算
中断源触发方式:A线程中调用threadB#interrupt()
方法.
实现机制也不难,扯淡以前咱们先思考两个问题:
问:
问题1
: 线程之间如何通讯,A线程的中断信号怎么才能传给线程B?
问题2
: 线程的状态有Running,Blocked,Waiting等,当线程B处在不一样的状态下,如何响应中断信号?
答:
问题1
:这种状况下线程之间通讯用共享内存就能够了。只须要给每一个线程都设置一个中断标示位, 这样A线程中调用threadB#interrupt()
方法,实际操做是把B线程的中断标示位设置为true。信号就算传递过去了
问题2
:当B线程处于非阻塞状态时,B线程能够在本身须要处理中断逻辑的地方判断中断标示位是否为true,就能够响应处理中断。
可是当B线程处于阻塞状态时,这特么怎么查本身的中断标示位啊?
JVM帮帮忙,当B线程阻塞在Object#wait()
,Thread#join()
,Thread#sleep()
,实现了InterruptibleChannel
接口的IO操做 和Selector
接口的select()
这些操做时,JVM会让B线程立刻抛出异常或被唤醒,从而让B线程能够选择是否响应中断。
由于是Java实现的中断机制,中断标示位的设置也是JVM帮作的。
信号:线程的中断标示位。
存储方式:JVM说了算。
JVM控制了信号的存储和让线程B及时唤醒。线程B控制了本身的中断响应逻辑,什么时候响应,如何响应。
获取信号:B线程可经过调用threadB#isInterrupted()
方法得知本身是否被中断,也就是经过本身主动拉取信号(poll方式)。
如何处理信号:B线程说了算。
处理完信号后作什么:B线程说了算。
Java的线程中断机制设计的比较灵活,使用者能够决定中断处理的较多事情。
总结下Java中和中断有关的方法:
在JDK中,线程池的ThreadPoolExecutor#shutdownNow()
方法就是调用workers线程数组中每一个worker线程的interrupt()
方法来关闭线程池。
这样暴力关闭线程会存在一个问题,线程池并不知道worker线程的中断执行状况,若是worker线程忽略了中断信号,那可能致使当前任务还在执行,发生意想不到的结果。
咱们再来看Java的中断机制,它其实只是提供了A线程给B线程发送中断信号。
考虑这么一种场景:当咱们执行一个大任务Task1
时,它太大了。咱们把它分为Task2
和Task3
,丢进线程池中处理。它们一样很大,咱们把他们分别分为Task4
,Task5
和Task6
,Task7
,一样丢进线程池中处理。
若是此时咱们想取消task1的执行,如何保证图中全部的worker都成功取消对应task的执行?
当咱们取消task1时,想要作的是取消全部task程序的继续运行
,而且可以得到全部task程序的取消结果
。
为何要强调task程序呢?由于worker可能并非只为一个task工做啊..好比task2的worker,它把task4和task5丢进线程池,就算完事了。若是咱们把取消task1变为取消task1的worker线程,可能会致使worker线程当前运行的非task1程序的失败。
咱们不太容易知道全部task程序当前运行的线程,咱们还须要知道全部task程序的运行结果。
只用Java的中断机制是知足不了咱们的需求的,可是咱们能够借鉴它的思路:
1.它用中断标示位记录线程是否应该中断
2.当线程阻塞时能够抛出异常
咱们这里要终止的是全部task程序的执行,因此咱们须要设计与task 强绑定的中断标示位
,能够有未中断
,中断中
,中断成功
,中断失败
四种 状态。为了让全部的线程均可以访问到,定义成全局共享变量就能够。
中断源和中断处理器之间经过task的中断标示位来通讯就能够。若是运行task程序的线程一直在阻塞,怎么唤醒它让它判断中断状态 呢?
对于咱们这个场景,咱们很难知道当前运行task程序的阻塞线程是谁。。能作的只是多安插中断判断点,这样当阻塞线程醒来后,再次判断task 的中断标示位,就能够响应中断了。
另:
唤醒一个线程只有Java的中断机制能够作,可是若是当前worker不是你能管理的线程池,那么它的中断处理逻辑就控制不了。
若是你能控制运行task的全部worker,并且worker在执行task时是同步得到结果的。那么能够结合与task强绑定的中断标示位
和Java中断机制
来作,这里前者的做用更可能是充当获取到任务的中断结果的做用。