中斷實際上是一種「中斷」事件,中斷具體表明什麼意思須要考慮它所處的上下文環境
和參照對象
是誰。考慮事件,咱們能夠簡單把中斷抽象爲這樣一種模型:數組
當咱們分析某種中斷事件時,咱們須要搞清楚這四個對象:異步
中斷源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中斷機制
來作,這裏前者的做用更可能是充當獲取到任務的中斷結果的做用。