我们都知道select()方法是阻塞的,只有在注册的channel有事件发生时
才会返回,但是如果程序需要立即返回怎么办呢,这你就涉及到一个wakeup()方法。该方法的作用就是让select()方法返回。
首先来看一下WindowsSelectorImpl对象的初始化过程
//在加载WindowsSelectorImpl类时创建一个管道
private final Pipe wakeupPipe = Pipe.open();
WindowsSelectorImpl(SelectorProvider var1) throws IOException {
super(var1);
//获取管道read端的文件描述符
this.wakeupSourceFd = ((SelChImpl)this.wakeupPipe.source()).getFDVal();
SinkChannelImpl var2 = (SinkChannelImpl)this.wakeupPipe.sink();
//获取管道write端的文件描述符
var2.sc.socket().setTcpNoDelay(true);
this.wakeupSinkFd = var2.getFDVal();
//将read端的文件描述符保存到pollWrapper数组的第一位
this.pollWrapper.addWakeupSocket(this.wakeupSourceFd, 0);
}
void addWakeupSocket(int var1, int var2) {
//添加句柄
this.putDescriptor(var2, var1);
//添加事件掩码(可读)
this.putEventOps(var2, 1);
}
接下来看看wakeup是如何让程序返回的
public Selector wakeup() {
Object var1 = this.interruptLock;
synchronized(this.interruptLock) {
//如果中断触发标志interruptTriggered为false
if (!this.interruptTriggered) {
//此方法的作用是向管道随机写入内容
this.setWakeupSocket();
this.interruptTriggered = true;
}
return this;
}
}
在初始化WindowsSelectorImpl对象的时候也向selector注册了管道的read端,并将事件掩码设为可读。因此当向管道的write端写入数据后,管道的read端会变为可读,selector轮询发现有事件被触发,就会返回。
wakeup方法的用处
当辅助线线程完成从poll方法返回后会触发threadFinished()方法,该方法的作用有三个
- 向管道中写入数据,让别的辅助线程也能够迅速返回。
- 更新待完成线程数
- 唤醒等待中的主线程
private synchronized void threadFinished() {
if (this.threadsToFinish == WindowsSelectorImpl.this.threads.size()) {
//如果此线程是第一个完成poll调用的,则通过管道让其余线程也返回(包括主线程)
WindowsSelectorImpl.this.wakeup();
}
--this.threadsToFinish;
if (this.threadsToFinish == 0) {
//如果所有的辅助线程都已经返回则唤醒处于等待状态的主线程
this.notify();
}
}
同样当主线程完成poll调用后也会触发wakeup方法的调用,具体如下:
- 当主线程率先完成poll调用后唤醒其他辅助线程
- 如果辅助线程没有全部返回则等待
private synchronized void waitForHelperThreads() {
//如果此线程是第一个完成poll调用的,则通过管道让其余线程也返回(包括主线程)
if (this.threadsToFinish == WindowsSelectorImpl.this.threads.size()) {
WindowsSelectorImpl.this.wakeup();
}
while(this.threadsToFinish != 0) {
try {
//如果辅助线程没有全部返回,则等待
WindowsSelectorImpl.this.finishLock.wait();
} catch (InterruptedException var2) {
Thread.currentThread().interrupt();
}
}
}
总结
主线程和辅助线程通过wakeup方法相互唤醒,具体规则如下:
- 当主线程率先完成poll调用时,进入waitForHelperThreads()方法,在该方法中唤醒所有辅助线程,并等待所有辅助线程完成后唤醒主线程
- 当一个辅助线程率先完成poll调用时,通过wakeup()方法唤醒其他线程。