SelectionKey
July 23, 2020 · View on GitHub
java.nio.channels.SelectionKey
java 的 nio 中,SelectionKey用四个标识来表示可进行某一事件(某个事件易已经发生)
下面通过源码理解它的实现技巧
代码
先看下源码:
// 插入一个事件
public abstract SelectionKey interestOps(int ops);
// 查询事件
public abstract int readyOps();
// 可进行读
public static final int OP_READ = 1 << 0;
// 可进行写
public static final int OP_WRITE = 1 << 2;
// 已经连接完成,可以进行读写
public static final int OP_CONNECT = 1 << 3;
// 可以接受新的连接
public static final int OP_ACCEPT = 1 << 4;
public final boolean isReadable() {
return (readyOps() & OP_READ) != 0;
}
public final boolean isWritable() {
return (readyOps() & OP_WRITE) != 0;
}
public final boolean isConnectable() {
return (readyOps() & OP_CONNECT) != 0;
}
public final boolean isAcceptable() {
return (readyOps() & OP_ACCEPT) != 0;
}
从上面的代码中可以看到,代码中用(readyOps() & OP_READ) != 0来表示可以进行某个事件操作(这里是读事件)。
那么这个是怎么实现的呢?思考为什么(readyOps() & OP_READ) != 0就表示可以进行读事件了?
位运算
首先要知道 & 是 java 的二进制位运算,只有二进制每一位的值都是 1 时,结果才是 1。(java 还支持其它位操作,可自己 Google)
即(二进制):
- 1 & 1 = 1
- 0 & 1 = 0
- 1 & 0 = 0
- 0 & 0 = 0
解读
下面的这个表格里面有每个标识的二进制和十进制
| readyOps | 十进制 | 二进制 |
|---|---|---|
| OP_READ | 1 | 00000001 |
| OP_WRITE | 4 | 00000100 |
| OP_CONNECT | 8 | 00001000 |
| OP_ACCEPT | 16 | 00010000 |
从表格中可看到:
OP_READ 的1在第一位
OP_WRITE 的1在第三位
OP_CONNECT 的1在第四位
OP_ACCEPT 的1在第五位
OP_READ & OP_WRITE 二进制的&结果(二进制00000000,十进制是零)
计算过程:
- OP_READ & OP_WRITE
- 00000001 # OP_READ
- 00000100 # OP_WRITE
- 00000000 # 结果
我们可以把这个四种值进行组合,他们&的结果都是零(二进制00000000),因为它们二进制形式的 1 都是在不同的位置上
因此只有readyOps()是本身,结果才不是零。即 OP_READ & OP_READ = 00000001 & 00000001结果是不是零(二进制00000001)
因此 (readyOps() & OP_READ) != 0 表示可以进行读事件
更进一步
如果 readyOps() 的二进制是00000101的时候,代表的什么事件?
00000101& OP_READ =00000101&00000001=0000000100000101& OP_WRITE =00000101&00000100=00000100
上面的运算结果都不是零,代表了读事件(OP_READ)&写(OP_WRITE)事件!
那么00000101是怎么来的,可通过 OP_READ | OP_WRITE = 00000001 | 00000100 = 00000101
我们可以通过interestOps(int ops)这个方法同时插入读事件&写事件
即: interestOps(OP_READ | OP_WRITE)