博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Selector.wakeup实现注记
阅读量:6367 次
发布时间:2019-06-23

本文共 2941 字,大约阅读时间需要 9 分钟。

    NIO中的Selector封装了底层的系统调用,其中wakeup用于唤醒阻塞在select方法上的线程,它的实现很简单,在linux上就是创建一个管道并加入poll的fd集合,wakeup就是往管道里写一个字节,那么阻塞的poll方法有数据可读就立即返回。证明这一点很简单,strace即可知道:
public
 
class
 SelectorTest {
    
public
 
static
 
void
 main(String[] args) 
throws
 Exception {
        Selector selector 
=
 Selector.open();
        selector.wakeup();
    }
}
     使用strace调用,只关心write的系统调用
sudo strace 
-
-
e write java SelectorTest
     输出:
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方法调用的,而不是其他方法呢,这个很好证明,我们多调用几次:
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();
    }
}
    修改程序调用三次wakeup,心细的朋友肯定注意到我们还调用了两次selectNow,这是因为在两次成功的select方法之间调用wakeup多次都只算做一次,为了显示3次write,这里就每次调用前select一下将前一次写入的字节读到,同样执行上面的strace调用,输出:
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还是等于一次:
public
 
class
 SelectorTest {
    
public
 
static
 
void
 main(String[] args) 
throws
 Exception {
        Selector selector 
=
 Selector.open();
        selector.wakeup();
        selector.wakeup();
        selector.wakeup();
    }
}
 
   输出:
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调用了 的interrupt方法:
public
  
void
 interrupt() 
{
        interrupt(outgoingInterruptFD);
}
    实际调用的是interrupt(fd)的native方法,查看EPollArrayWrapper.c可见清晰的write系统调用:
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
"
);
    }
}
    写入一个字节的fakebuf。有朋友问起这个问题,写个注记在此。strace充分利用对了解这些细节很有帮助。
  文章转自庄周梦蝶  ,原文发布时间 2010-10-22

转载地址:http://xdgma.baihongyu.com/

你可能感兴趣的文章
localStorage应用(写的时间缓存在本地浏览器)
查看>>
Javascript编码规范
查看>>
laravel-admin 使用记录(2) - 快速搭建 CURD
查看>>
自定义SpringBoot项目的Maven原型
查看>>
solidity不支持动态扩容
查看>>
5.java String对象
查看>>
基于 less,sass,stylus三种预处理rem
查看>>
微信棋牌游戏域名防封最新解决方案
查看>>
第十八天-企业应用架构模式-基本模式
查看>>
Promise加载图片用法详解
查看>>
《CSS设计指南》读书笔记
查看>>
win10初始化mysql5.7.24
查看>>
Windows 下安装 SCWS
查看>>
每日两道前端面试题
查看>>
什么是产品Backlog修饰 (Backlog Grooming)?
查看>>
Java反射的基本使用
查看>>
破坏程序员生产力的 12 件事
查看>>
什么是SOLID原则(第1部分)
查看>>
动态规划 python 挖矿问题
查看>>
Angular material中自定义分页信息
查看>>