NIO中的Selector封裝了底層的系統調用,其中wakeup用於喚醒阻塞在select方法上的線程,它的實現很簡單,在linux上就是建立一個管道並加入poll的fd集合,wakeup就是往管道里寫一個字節,那麼阻塞的poll方法有數據可讀就當即返回。證實這一點很簡單,strace便可知道:java
[java] public class SelectorTest { public static void main(String[] args) throws Exception { Selector selector = Selector.open(); selector.wakeup(); } } [/java]
使用strace調用,只關心write的系統調用linux
「 sudo strace -f -e write java SelectorTest
輸出:app
「 Process 29181 attached Process 29182 attached Process 29183 attached Process 29184 attached Process 29185 attached Process 29186 attached Process 29187 attached Process 29188 attached Process 29189 attached Process 29190 attached Process 29191 attached [pid 29181] write(36, 「1″, 1) = 1 Process 29191 detached Process 29184 detached Process 29181 detached
有的同窗說了,怎麼證實這個write是wakeup方法調用的,而不是其餘方法呢,這個很好證實,咱們多調用幾回:ide
[java] public class SelectorTest { public static void main(String[] args) throws Exception { Selector selector = Selector.open(); selector.wakeup(); selector.selectNow(); selector.wakeup(); selector.selectNow(); selector.wakeup(); } } [/java]
修改程序調用三次wakeup,心細的朋友確定注意到咱們還調用了兩次selectNow,這是由於在兩次成功的select方法之間調用wakeup屢次都只算作一次,爲了顯示3次write,這裏就每次調用前select一下將前一次寫入的字節讀到,一樣執行上面的strace調用,輸出:this
「 Process 29303 attached Process 29304 attached Process 29305 attached Process 29306 attached Process 29307 attached Process 29308 attached Process 29309 attached Process 29310 attached Process 29311 attached Process 29312 attached Process 29313 attached [pid 29303] write(36, 「1″, 1) = 1 [pid 29303] write(36, 「1″, 1) = 1 [pid 29303] write(36, 「1″, 1) = 1 Process 29313 detached Process 29309 detached Process 29306 detached Process 29303 detached
果真是3次write的系統調用,都是寫入一個字節,若是咱們去掉selectNow,那麼三次wakeup仍是等於一次:線程
[java] public class SelectorTest { public static void main(String[] args) throws Exception { Selector selector = Selector.open(); selector.wakeup(); selector.wakeup(); selector.wakeup(); } } [/java]
輸出:ip
「 Process 29331 attached Process 29332 attached Process 29333 attached Process 29334 attached Process 29335 attached Process 29336 attached Process 29337 attached Process 29338 attached Process 29339 attached Process 29340 attached Process 29341 attached [pid 29331] write(36, 「1″, 1) = 1 Process 29341 detached Process 29337 detached Process 29334 detached Process 29331 detached
wakeup方法的API說明沒有欺騙咱們。wakeup方法的API還告訴咱們,若是當前Selector沒有阻塞在select方法上,那麼本次wakeup調用會在下一次select阻塞的時候生效,這個道理很簡單,wakeup方法寫入一個字節,下次poll等待的時候當即發現可讀並返回,所以不會阻塞。源碼
具體到源碼級別,在linux平臺上的wakeup方法其實調用了pipe建立了管道,wakeup調用了EPollArrayWrapper的interrupt方法:it
[java] public void interrupt() { interrupt(outgoingInterruptFD); } [/java] 實際調用的是interrupt(fd)的native方法,查看EPollArrayWrapper.c可見清晰的write系統調用: [java] JNIEXPORT void JNICALL Java_sun_nio_ch_EPollArrayWrapper_interrupt(JNIEnv *env, jobject this, jint fd) { int fakebuf[1]; fakebuf[0] = 1; if (write(fd, fakebuf, 1) < 0) { JNU_ThrowIOExceptionWithLastError(env,"write to interrupt fd failed"); } } [/java]
寫入一個字節的fakebuf。有朋友問起這個問題,寫個註記在此。strace充分利用對了解這些細節頗有幫助。pip