日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > java >内容正文

java

java nio wakeup_Java NIO的wakeup剖析

發(fā)布時(shí)間:2023/12/20 java 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java nio wakeup_Java NIO的wakeup剖析 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

java NIO的實(shí)現(xiàn)中,有不少細(xì)節(jié)點(diǎn)非常有學(xué)習(xí)意義的,就好比下面的這個(gè)點(diǎn):

Selector的 wakeup原理是什么?是如何實(shí)現(xiàn)的?

wakeup()

準(zhǔn)確來(lái)說(shuō),應(yīng)該是Selector的wakeup(),即Selector的喚醒,為什么要有這個(gè)喚醒操作呢?那還得從Selector的選擇方式 來(lái)說(shuō)明,前文已經(jīng)總結(jié)過(guò)Selector的選擇方式有三種:select()、select(timeout)、selectNow()。

selectNow的選擇過(guò)程是非阻塞的,與wakeup沒(méi)有太大關(guān)系。

select(timeout)和select()的選擇過(guò)程是阻塞的,其他線(xiàn)程如果想終止這個(gè)過(guò)程,就可以調(diào)用wakeup來(lái)喚醒。

wakeup的原理

既然Selector阻塞式選擇因?yàn)檎业礁信d趣事件ready才會(huì)返回(排除超時(shí)、中斷),就給它構(gòu)造一個(gè)感興趣事件ready的場(chǎng)景即可。下圖可以比較形象的形容wakeup原理:

Selector管轄的FD(文件描述符,linux即為fd,對(duì)應(yīng)一個(gè)文件,windows下對(duì)應(yīng)一個(gè)句柄;每個(gè)可選擇Channel在創(chuàng)建的時(shí) 候,就生成了與其對(duì)應(yīng)的FD,Channel與FD的聯(lián)系見(jiàn)另一篇)中包含某一個(gè)FD A, A對(duì)數(shù)據(jù)可讀事件感興趣,當(dāng)往圖中漏斗端放入(寫(xiě)入)數(shù)據(jù),數(shù)據(jù)會(huì)流進(jìn)A,于是A有感興趣事件ready,最終,select得到結(jié)果而返回。

wakeup在Selector中的定義如下:publicabstractSelector?wakeup();

下面結(jié)合上圖來(lái)追尋wakeup的實(shí)現(xiàn):

linux下Selector默認(rèn)實(shí)現(xiàn)為PollSelectorImpl,當(dāng)內(nèi)核版本大于2.6時(shí),實(shí)現(xiàn)為EPollSelectorImpl,僅看這兩者的wakeup方法,代碼似乎完全一樣:publicSelector?wakeup()?{

synchronized(interruptLock)?{

if(!interruptTriggered)?{

pollWrapper.interrupt();

interruptTriggered?=?true;

}

}

returnthis;

}

window下Selector的實(shí)現(xiàn)為WindowsSelectorImpl,其wakeup實(shí)現(xiàn)如下:publicSelector?wakeup()?{

synchronized(interruptLock)?{

if(!interruptTriggered)?{

setWakeupSocket();

interruptTriggered?=?true;

}

}

returnthis;

}

其中interruptTriggered為中斷已觸發(fā)標(biāo)志,當(dāng)pollWrapper.interrupt()之后,該標(biāo)志即為true了;得益于這個(gè)標(biāo)志,連續(xù)兩次wakeup,只會(huì)有一次效果。

對(duì)比上圖及上述代碼,其實(shí)pollWrapper.interrupt()及setWakeupSocket()就是圖中的往漏斗中倒水的過(guò)程,不 管windows也好,linux也好,它們wakeup的思想是完全一致的,不同的地方就在于實(shí)現(xiàn)的細(xì)節(jié)了,例如上圖中漏斗與通道的鏈接部 分,linux下是采用管道pipe來(lái)實(shí)現(xiàn)的,而windows下是采用兩個(gè)socket之間的通訊來(lái)實(shí)現(xiàn)的,它們都有這樣的特性:

1)都有兩個(gè)端,一個(gè) 是read端,一個(gè)是write端,windows中兩個(gè)socket也是一個(gè)扮演read的角色,一個(gè)扮演write的角色;

2)當(dāng)往write端寫(xiě)入 數(shù)據(jù),則read端即可以收到數(shù)據(jù);從它們的特性可以看出,它們是能夠勝任這份工作的。

如果只想理解wakeup的原理,看到這里應(yīng)該差不多了,不過(guò),下面,想繼續(xù)深入一下,滿(mǎn)足更多人的好奇心。

先看看linux下PollSelector的具體wakeup實(shí)現(xiàn),分階段來(lái)介紹:

1)準(zhǔn)備階段

PollSelector在構(gòu)造的時(shí)候,就將管道pipe,及wakeup專(zhuān)用FD給準(zhǔn)備好,可以看一下它的實(shí)現(xiàn):PollSelectorImpl(SelectorProvider?sp)?{

super(sp,?1,?1);

int[]?fdes?=newint[2];

IOUtil.initPipe(fdes,?false);

fd0?=?fdes[0];

fd1?=?fdes[1];

pollWrapper?=?newPollArrayWrapper(INIT_CAP);

pollWrapper.initInterrupt(fd0,?fd1);

channelArray?=?newSelectionKeyImpl[INIT_CAP];

}

IOUtil.initPipe,采用系統(tǒng)調(diào)用pipe(int fd[2])來(lái)創(chuàng)建管道,fd[0]即為ready端,fd[1]即為write端。

另一個(gè)需要關(guān)注的點(diǎn)就是pollWrapper.initInterrupt(fd0, fd1),先看一下它的實(shí)現(xiàn):voidinitInterrupt(intfd0,intfd1)?{

interruptFD?=?fd1;

putDescriptor(0,?fd0);

putEventOps(0,?POLLIN);

putReventOps(0,0);

}

以看到,initInterrupt在準(zhǔn)備wakeup專(zhuān)用FD,因?yàn)閒d0是read端fd,fd1是write端fd:

interruptFD被初始化為write端fd;

putDescriptor(0, fd0)初始化pollfd數(shù)組中的第一個(gè)pollfd,即指PollSelector關(guān)注的第一個(gè)fd,即為fd0;

putEventOps(0, POLLIN)初始化fd0對(duì)應(yīng)pollfd中的events為POLLIN,即指fd0對(duì)可讀事件感興趣;

putReventOps(0, 0)只是初始化一下fd0對(duì)應(yīng)的pollfd中的revents;

2)執(zhí)行階段

有了前面的準(zhǔn)備工作,就看PollArrayWrapper中的interrupt()實(shí)現(xiàn):publicvoidinterrupt()?{

interrupt(interruptFD);

}

interrupt是native方法,它的入?yún)nterruptFD即為準(zhǔn)備階段管道的write端fd,對(duì)應(yīng)于上圖,其實(shí)就是漏斗端,因此,就是不看其實(shí)現(xiàn),也知道它肯定扮演著倒水的這個(gè)動(dòng)作,看其實(shí)現(xiàn):JNIEXPORTvoidJNICALL

Java_sun_nio_ch_PollArrayWrapper_interrupt(JNIEnv?*env,?jobject?this,?jint?fd)

{

intfakebuf[1];

fakebuf[0]?=?1;

if(write(fd,?fakebuf,?1)?

JNU_ThrowIOExceptionWithLastError(env,

"Write?to?interrupt?fd?failed");

}

}

可以看出,interrupt(interruptFD)是往管道的write端fd1中寫(xiě)入一個(gè)字節(jié)(write(fd, fakebuf, 1))。

是的,只需要往fd1中寫(xiě)入一個(gè)字節(jié),fd0即滿(mǎn)足了可讀事件ready,則Selector自然會(huì)因?yàn)橛惺录eady而中止阻塞返回。

EPollSelector與PollSelector相比,其wakeup實(shí)現(xiàn)就只有initInterrupt不同,它的實(shí)現(xiàn)如下:voidinitInterrupt(intfd0,intfd1)?{

outgoingInterruptFD?=?fd1;

incomingInterruptFD?=?fd0;

epollCtl(epfd,?EPOLL_CTL_ADD,?fd0,?EPOLLIN);

}

epfd之前的篇章里已經(jīng)講過(guò),它是通過(guò)epoll_create創(chuàng)建出來(lái)的epoll文件fd,epollCtl調(diào)用內(nèi)核epoll_ctl實(shí)現(xiàn)了往epfd上添加fd0,且其感興趣事件為可讀(EPOLLIN)。

因此可以斷定,EPollSelector與PollSelector的wakeup實(shí)現(xiàn)是一致的。

因?yàn)橹耙恢睂?zhuān)注與分析linux下的Java NIO實(shí)現(xiàn),忽略了windows下的選擇過(guò)程等,這里突然講解其wakeup實(shí)現(xiàn)似乎很突兀,所以打算后面專(zhuān)門(mén)起一篇來(lái)介紹windows下的NIO實(shí) 現(xiàn),這里我們只需要理解wakeup原理,甚至自己去看看其wakeup實(shí)現(xiàn),應(yīng)該也沒(méi)什么難度。

關(guān)于wakeup,這里還有兩個(gè)疑問(wèn):

為什么wakeup方法返回Selector?

windows下也是有pipe的,為什么使用socket而不是使用pipe來(lái)實(shí)現(xiàn)wakeup的?

也歡迎大家留下自己的想法,一起討論。

原文鏈接:http://goldendoc.iteye.com/blog/1152079

【編輯推薦】Java NIO類(lèi)庫(kù)關(guān)系圖解

淺析Tomcat NIO 配置

Java NIO API詳解

Java NIO基本使用實(shí)例

Java NIO的介紹及工作原理

推薦閱讀

下面這張圖給出了nio類(lèi)庫(kù)的各個(gè)類(lèi)之間的關(guān)系,這樣你就能知道該怎樣移動(dòng)和轉(zhuǎn)換數(shù)據(jù)了。舉例來(lái)說(shuō),如果你想把byte數(shù)組寫(xiě)進(jìn)文件,你得先用ByteBuffer.wrap( )方法把這個(gè)byte數(shù)組wrap成buffer,再用getChannel( )在File>>>詳細(xì)閱讀

地址:http://www.17bianji.com/kaifa2/Java/1618.html

總結(jié)

以上是生活随笔為你收集整理的java nio wakeup_Java NIO的wakeup剖析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。