java io中斷_JDK源碼閱讀:InterruptibleChannel 與可中斷 IO

來源:木杉的博客 ,

imushan.com/2018/08/01/java/language/JDK源碼閱讀-InterruptibleChannel與可中斷IO/

Java傳統IO是不支持中斷的,所以如果代碼在read/write等操作阻塞的話,是無法被中斷的。這就無法和Thead的interrupt模型配合使用了。JavaNIO眾多的升級點中就包含了IO操作對中斷的支持。InterruptiableChannel表示支持中斷的Channel。我們常用的FileChannel,SocketChannel,DatagramChannel都實現了這個接口。

InterruptibleChannel接口

public interface InterruptibleChannel extends Channel{

/**

* 關閉當前Channel

*

* 任何當前阻塞在當前channel執行的IO操作上的線程,都會收到一個AsynchronousCloseException異常

*/

public void close() throws IOException;

}

InterruptibleChannel接口沒有定義任何方法,其中的close方法是父接口就有的,這里只是添加了額外的注釋。

AbstractInterruptibleChannel實現了InterruptibleChannel接口,并提供了實現可中斷IO機制的重要的方法,比如begin(),end()。

在解讀這些方法的代碼前,先了解一下NIO中,支持中斷的Channel代碼是如何編寫的。

第一個要求是要正確使用begin()和end()方法:

boolean completed = false;

try {

begin();

completed = ...; ? ?// 執行阻塞IO操作

return ...; ? ? ? ? // 返回結果

} finally {

end(completed);

}

NIO規定了,在阻塞IO的語句前后,需要調用begin()和end()方法,為了保證end()方法一定被調用,要求放在finally語句塊中。

第二個要求是Channel需要實現java.nio.channels.spi.AbstractInterruptibleChannel#implCloseChannel這個方法。AbstractInterruptibleChannel在處理中斷時,會調用這個方法,使用Channel的具體實現來關閉Channel。

接下來我們具體看一下begin()和end()方法是如何實現的。

begin方法

// 保存中斷處理對象實例

private Interruptible interruptor;

// 保存被中斷線程實例

private volatile Thread interrupted;

protected final void begin(){

// 初始化中斷處理對象,中斷處理對象提供了中斷處理回調

// 中斷處理回調登記被中斷的線程,然后調用implCloseChannel方法,關閉Channel

if (interruptor == null) {

interruptor = new Interruptible() {

public void interrupt(Thread target){

synchronized (closeLock) {

// 如果當前Channel已經關閉,則直接返回

if (!open)

return;

// 設置標志位,同時登記被中斷的線程

open = false;

interrupted = target;

try {

// 調用具體的Channel實現關閉Channel

AbstractInterruptibleChannel.this.implCloseChannel();

} catch (IOException x) { }

}

}};

}

// 登記中斷處理對象到當前線程

blockedOn(interruptor);

// 判斷當前線程是否已經被中斷,如果已經被中斷,可能登記的中斷處理對象沒有被執行,這里手動執行一下

Thread me = Thread.currentThread();

if (me.isInterrupted())

interruptor.interrupt(me);

}

從begin()方法中,我們可以看出NIO實現可中斷IO操作的思路,是在Thread的中斷邏輯中,掛載自定義的中斷處理對象,這樣Thread對象在被中斷時,會執行中斷處理對象中的回調,這個回調中,執行關閉Channel的操作。這樣就實現了Channel對線程中斷的響應了。

接下來重點就是研究“Thread添加中斷處理邏輯”這個機制是如何實現的了,是通過blockedOn方法實現的:

static void blockedOn(Interruptible intr){ ? ? ? ? // package-private

sun.misc.SharedSecrets.getJavaLangAccess().blockedOn(Thread.currentThread(),intr);

}

blockedOn方法使用的是JavaLangAccess的blockedOn方法。

SharedSecrets是一個神奇而糟糕的類,為啥說是糟糕呢,因為這個方法的存在,就是為了訪問JDK類庫中一些因為類作用域限制而外部無法訪問的類或者方法。JDK很多類與方法是私有或者包級別私有的,外部是無法訪問的,但是JDK在本身實現的時候又存在互相依賴的情況,所以為了外部可以不依賴反射訪問這些類或者方法,在sun包下,存在這么一個類,提供了各種超越限制的方法。

SharedSecrets.getJavaLangAccess()方法返回JavaLangAccess對象。JavaLangAccess對象就和名稱所說的一樣,提供了java.lang包下一些非公開的方法的訪問。這個類在System初始化時被構造:

// java.lang.System#setJavaLangAccess

private static void setJavaLangAccess(){

sun.misc.SharedSecrets.setJavaLangAccess(new sun.misc.JavaLangAccess(){

public void blockedOn(Thread t, Interruptible b){

t.blockedOn(b);

}

//...

});

}

可以看出,sun.misc.JavaLangAccess#blockedOn保證的就是java.lang.Thread#blockedOn這個包級別私有的方法:

/* The object in which this thread is blocked in an interruptible I/O

* operation, if any. ?The blocker's interrupt method should be invoked

* after setting this thread's interrupt status.

*/

private volatile Interruptible blocker;

private final Object blockerLock = new Object();

/* Set the blocker field; invoked via sun.misc.SharedSecrets from java.nio code

*/

void blockedOn(Interruptible b){

// 串行化blocker相關操作

synchronized (blockerLock) {

blocker = b;

}

}

而這個方法也非常簡單,就是設置java.lang.Thread#blocker變量為之前提到的中斷處理對象。而且從注釋中可以看出,這個方法就是專門為NIO設計的,注釋都非常直白的提到了,NIO的代碼會通過sun.misc.SharedSecrets調用到這個方法。。

接下來就是重頭戲了,看一下Thread在中斷時,如何調用NIO注冊的中斷處理器:

public void interrupt(){

if (this != Thread.currentThread())

checkAccess();

synchronized (blockerLock) {

Interruptible b = blocker;

// 如果NIO設置了中斷處理器,則只需Thread本身的中斷邏輯后,調用中斷處理器的回調函數

if (b != null) {

interrupt0(); ? ? ? ? ? // 這一步會設置interrupt標志位

b.interrupt(this);

return;

}

}

// 如果沒有的話,就走普通流程

interrupt0();

}

68bfdf3476ef0c6acd06a67b23f67ce1.gif

end方法

begin()方法負責添加Channel的中斷處理器到當前線程。end()是在IO操作執行完/中斷完后的操作,負責判斷中斷是否發生,如果發生判斷是當前線程發生還是別的線程中斷把當前操作的Channel給關閉了,對于不同的情況,拋出不同的異常。

protected final void end(boolean completed) throws AsynchronousCloseException{

// 清空線程的中斷處理器引用,避免線程一直存活導致中斷處理器無法被回收

blockedOn(null);

Thread interrupted = this.interrupted;

if (interrupted != null && interrupted == Thread.currentThread()) {

interrupted = null;

throw new ClosedByInterruptException();

}

// 如果這次沒有讀取到數據,并且Channel被另外一個線程關閉了,則排除Channel被異步關閉的異常

// 但是如果這次讀取到了數據,就不能拋出異常,因為這次讀取的數據是有效的,需要返回給用戶的(重要邏輯)

if (!completed && !open)

throw new AsynchronousCloseException();

}

通過代碼可以看出,如果是當前線程被中斷,則拋出ClosedByInterruptException異常,表示Channel因為線程中斷而被關閉了,IO操作也隨之中斷了。

如果是當前線程發現Channel被關閉了,并且是讀取還未執行完畢的情況,則拋出AsynchronousCloseException異常,表示Channel被異步關閉了。

end()邏輯的活動圖如下:

68bfdf3476ef0c6acd06a67b23f67ce1.gif

場景分析

并發的場景分析起來就是復雜,上面的代碼不多,但是場景很多,我們以sun.nio.ch.FileChannelImpl#read(java.nio.ByteBuffer)為例分析一下可能的場景:

A線程read,B線程中斷A線程:A線程拋出ClosedByInterruptException異常

A,B線程read,C線程中斷A線程

A被中斷時,B剛剛進入read方法:A線程拋出ClosedByInterruptException異常,B線程ensureOpen方法拋出ClosedChannelException異常

A被中斷時,B阻塞在底層read方法中:A線程拋出ClosedByInterruptException異常,B線程底層方法拋出異常返回,end方法中拋出AsynchronousCloseException異常

A被中斷時,B已經讀取到數據:A線程拋出ClosedByInterruptException異常,B線程正常返回

sun.nio.ch.FileChannelImpl#read(java.nio.ByteBuffer)代碼如下:

public int read(ByteBuffer dst) throws IOException{

ensureOpen(); ?// 1

if (!readable) // 2

throw new NonReadableChannelException();

synchronized (positionLock) {

int n = 0;

int ti = -1;

try {

begin();

ti = threads.add();

if (!isOpen())

return 0; // 3

do {

n = IOUtil.read(fd, dst, -1, nd); // 4

} while ((n == IOStatus.INTERRUPTED) && isOpen());

return IOStatus.normalize(n);

} finally {

threads.remove(ti);

end(n > 0);

assert IOStatus.check(n);

}

}

}

總結

在JavaIO時期,人們為了中斷IO操作想了不少方法,核心操作就是關閉流,促使IO操作拋出異常,達到中斷IO的效果。NIO中,將這個操作植入了java.lang.Thread#interrupt方法,免去用戶自己編碼特定代碼的麻煩。使IO操作可以像其他可中斷方法一樣,在中斷時拋出ClosedByInterruptException異常,業務程序捕獲該異常即可對IO中斷做出響應。

參考資料

java – What does JavaLangAccess.blockedOn(Thread t, Interruptible b) do? – Stack Overflow

https://stackoverflow.com/questions/8544891/what-does-javalangaccess-blockedonthread-t-interruptible-b-do

Java NIO 那些躲在角落的細節

https://www.oschina.net/question/138146_26027

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/534457.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/534457.shtml
英文地址,請注明出處:http://en.pswp.cn/news/534457.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java值棧_Struts2學習筆記-Value Stack(值棧)和OGNL表達式

只是本人的Struts2學習筆記,關于Value Stack(值棧)和OGNL表達式,把我知道的都說出來,希望對大家有用。一,值棧的作用記錄處理當前請求的action的數據。二,小例子有兩個action:Action1和Action2Action1有兩個…

php項目實戰流程_一個完整的php流程管理實例代碼分享

1. 添加新流程頁面:請選擇流程節點:session_start();include("../DBDA.class.php");$db new DBDA();$suser "select * from users";$auser $db->Query($suser);foreach($auser as $v){echo " {$v[2]} ";}?>$att…

php cdata,PHPcdata處理(詳細介紹)_PHP教程

PHPcdata處理(詳細介紹)_PHP教程當時在網上找了一個CDATA的轉換器, 修改之后, 將CDATA標簽給過濾掉。如下代碼如下:// States://// out// // // // // // // // in// ]// ]]//// (Yes, the states a represented by strings.)//$state out;$a s…

PHP 與go 通訊,Golang和php通信

不同語言之間的通信方式有很多種,這里我介紹一種最簡單通信方式,json-rpc。Golang自帶json-rpc包,使用起來十分簡單,示例如下,提供一個簡單echo server。package mainimport ("fmt""net""net…

php 接口日志,PHP 開發 APP 接口--錯誤日志接口

APP 上線以后可能遇到的問題:① APP 強退② 數據加載失敗③ APP 潛在問題錯誤日志需要記錄的內容數據表 error_log 字段:idapp_id:app 類別 iddid:客戶端設備號version_id:版本號version_mini:小版本號erro…

php 空模塊,tp5.1配置空模塊,空方法

config/app.php//默認的空模塊名empty_module>index,controller/Error.php<?php namespace app\index\controller;use Env;use think\Controller;class Error extends Controller {//Db::connect(db_ck)//全局MISS路由 在route.php里面設置找不到控制器默認處理//Route:…

centos7php自啟動,centos7系統下nginx安裝并配置開機自啟動操作

這篇文章主要介紹了centos7系統下nginx安裝并配置開機自啟動操作方法,非常不錯&#xff0c;具有參考借鑒價值&#xff0c;需要的朋友可以參考下這篇文章主要介紹了centos7系統下nginx安裝并配置開機自啟動操作方法,非常不錯&#xff0c;具有參考借鑒價值&#xff0c;需要的朋友…

時鐘php,php+js液晶時鐘

php代碼$size_small5;//液晶寬度$size_big25;//液晶長度$distance10;//間距$color_back"#DDDDDD";$color_dark"#CCCCCC";$color_light"#000000";$number0;?>Timer|www.ibtf.net|www.bitefu.netfunction swapcolor(obj,onoff)//改變顏色{if (…

r和matlab學哪個,初學者求教‘r*’是什么意思啊

該樓層疑似違規已被系統折疊 隱藏此樓查看此樓PLOT(X,Y,S) where S is a character string made from one elementfrom any or all the following 3 columns:b blue . point - solidg green o circle : dottedr red x x-mark -. dashdotc cyan plus -- dashedm magenta * star…

php swoole 心跳,聊聊swoole的心跳

來自&#xff1a;桶哥的一篇關于swoole的心跳的文章&#xff0c;作為Swoole顧問(顧得上就問,是為「顧問」)得推一下這篇文章&#xff0c;最后只留下一配置&#xff0c;其實我也不是太明白原理&#xff0c;我在想如果是局域網里還需要心跳&#xff1f;—————————————…

mysql 查詢 投影,MySql-連接查詢

連接查詢Chloe 友好支持多表連接查詢&#xff0c;一切都可以用 lambda 表達式操作&#xff0c;返回類型可以是自定義類型&#xff0c;也可以是匿名類型。強類型開發&#xff0c;編譯可見錯誤&#xff0c;容錯率高。1.建立連接&#xff1a;var user_city_province context.Quer…

php 遞歸欄目名疊加,thinkPHP實現遞歸循環欄目并按照樹形結構無限極輸出的方法,thinkphp遞歸...

thinkPHP實現遞歸循環欄目并按照樹形結構無限極輸出的方法&#xff0c;thinkphp遞歸本文實例講述了thinkPHP實現遞歸循環欄目并按照樹形結構無限極輸出的方法。分享給大家供大家參考&#xff0c;具體如下&#xff1a;這里使用thinkphp遞歸循環欄目按照樹形結構無限極輸出&#…

php cannot call constructor,安裝ECshop普遍問題的解決方法

安裝時的問題&#xff1a;1.Strict Standards: Non-static method cls_image::gd_version() should not be called statically in /usr/local/httpd2/htdocs/upload/install/includes/lib_installer.php on line 31解決&#xff1a;找到install/includes/lib_installer.php中的…

wind試用版 matlab,免費產品試用 - MATLAB Simulink

請選擇其一AlabamaAlaska美屬薩摩亞APO/FPO AAAPO/FPO AEAPO/FPO APArizonaArkansasCaliforniaCaroline IslandsColoradoConnecticutDelawareDistrict of ColumbiaFlorida格魯吉亞關島HawaiiIdahoIllinoisIndianaIowaKansasKentuckyLouisianaMaineMariana Islands馬紹爾群島Mar…

php yii2 sns,GitHub - yggphpcoder/iisns: 基于 yii2 的 sns 社區系統,一站式解決社區建站...

iisns - 地球村入口iiSNS 是基于 yii2 的 SNS 社區系統&#xff0c;一站式解決社區建站。可以寫文章&#xff0c;做記錄&#xff0c;上傳圖片&#xff0c;論壇聊天等。還可以用來做內容管理系統(CMS)。iiSNS 是一個免費的開源項目&#xff0c;在 MIT 許可證下授權發布。特點與功…

php mvc 商城,基于MVC框架的小型網上商城設計

2&#xff0e;本人對課題任務書提出的任務要求及實現預期目標的可行性分析基于MVC框架的小型網上商城實現的功能&#xff1a;商品的瀏覽、查詢、購買&#xff0c;會員注冊以及會員訂單的查詢等&#xff0c;方便商場活動&#xff0c;該系統基本實現了網上商城的應有功能。該系統…

php 做更新進度條,PHP exec()后更新Bootstrap進度條

我使用PHP來運行一個python腳本&#xff0c;并且在腳本執行后需要更新一個進度條。進度條更新后&#xff0c;將執行另一個腳本&#xff0c;依此類推。這里是我的代碼如此的票價。我試圖用JavaScript來實現。它沒有解決Button Textif (isset($_POST[turn])){exec("sudo pyt…

zblog php和asp功能,ZBlog是否適合PHP或ASP?我們該如何選擇?

我最近玩了zblog一段時間&#xff0c;對于大多數第一次聯系zblog的博客&#xff0c;他們會問zblog是否適合PHP或ASP&#xff1f;我們該如何選擇&#xff1f;事實上&#xff0c;我真的不明白這個問題。我個人更喜歡PHP。今天我將整理出來并對PHP版本和ASP版本進行比較&#xff0…

php js記住密碼功能,jquery.cookie.js實現用戶登錄保存密碼功能的方法_jquery

本文實例講述了jquery.cookie.js實現用戶登錄保存密碼功能的方法。分享給大家供大家參考&#xff0c;具體如下&#xff1a;需要導入的js有jquery.js和jquery.cookie.js在頁面加載時首先嘗試獲取cookie的值&#xff0c;如果cookie有值&#xff0c;則將獲取到的值填入輸入框中&am…

oracle dbf 超大,oracle?數據庫users01.dbf文件過大?轉移方法

如果出現 linux 拒絕錯誤&#xff0c;可以把目錄權限 該為777由于在安裝的時候將Oracle安裝到了C盤&#xff0c;表空間也創建到了C盤(當時沒有在意)&#xff0c;等項目進行到了中期&#xff0c;發現C盤的空間不夠用了。此時&#xff0c;一個較好的解決辦法就是將表空間的文件轉…