Java 选择器.select(超时)在超时之前返回0
据Javadoc说, 它仅在至少选择一个通道、调用此选择器的唤醒方法、中断当前线程或给定的超时期限到期(以先到者为准)后返回 但有时它会在没有以下4种情况的情况下返回:Java 选择器.select(超时)在超时之前返回0,java,nio,Java,Nio,据Javadoc说, 它仅在至少选择一个通道、调用此选择器的唤醒方法、中断当前线程或给定的超时期限到期(以先到者为准)后返回 但有时它会在没有以下4种情况的情况下返回: 至少选择了一个通道:它返回0 已调用wakeup方法:未调用wakeup 当前线程被中断:thread.interrupted()返回false 给定超时期限到期:根据日志未到期 更新日期2016-03-15 在第392行和第402行的源代码中,我添加了一些日志: 这很奇怪:没有选择的键,没有中断,没有超时,也没有唤醒,但它返
未调用wakeup
select(int timeout)
方法部分(我的重点):
返回:
- 已更新其就绪操作集的键数,可能为零
这可能真的是JDK中的一个bug。Netty和Mina似乎也遇到了这样的问题,他们将选择器重建为一种解决方法 见最新净代码L641-681:
if (selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks() || hasScheduledTasks()) {
// - Selected something,
// - waken up by user, or
// - the task queue has a pending task.
// - a scheduled task is ready for processing
break;
}
...
} else if (SELECTOR_AUTO_REBUILD_THRESHOLD > 0 &&
selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) {
// The selector returned prematurely many times in a row.
// Rebuild the selector to work around the problem.
logger.warn(
"Selector.select() returned prematurely {} times in a row; rebuilding selector.",
selectCnt);
rebuildSelector();
selector = this.selector;
// Select again to populate selectedKeys.
selector.selectNow();
selectCnt = 1;
break;
}
见Mina 2.0代码L1070-1092:
if (!wakeupCalled.getAndSet(false) && (selected == 0) && (delta < 100)) {
// Last chance : the select() may have been
// interrupted because we have had an closed channel.
if (isBrokenConnection()) {
LOG.warn("Broken connection");
} else {
LOG.warn("Create a new selector. Selected is 0, delta = " + (t1 - t0));
// Ok, we are hit by the nasty epoll
// spinning.
// Basically, there is a race condition
// which causes a closing file descriptor not to be
// considered as available as a selected channel,
// but
// it stopped the select. The next time we will
// call select(), it will exit immediately for the
// same
// reason, and do so forever, consuming 100%
// CPU.
// We have to destroy the selector, and
// register all the socket on a new one.
registerNewSelector();
}
}
if(!wakeupCalled.getAndSet(false)&&(selected==0)&(delta<100)){
//最后一次机会:select()可能已被删除
//因为我们有一个封闭的通道而中断。
如果(isBrokenConnection()){
日志警告(“断开的连接”);
}否则{
LOG.warn(“创建一个新选择器。所选为0,delta=“+(t1-t0));
//好吧,我们被可怕的厄波尔击中了
//旋转。
//基本上,有一个种族条件
//这会导致关闭文件描述符不可用
//视为可用的选定频道,
//但是
//它停止了选择。下次我们将
//调用select(),它将立即退出
//同样的
//理性,永远如此,消耗100%
//中央处理器。
//我们必须摧毁选择器,然后
//将所有套接字注册到新的套接字上。
registerNewSelector();
}
}
因此,如果select()返回意外的零,则注册新选择器可能是最佳做法。在我的程序中,一旦选中,所有SelectedKey都将被处理和清除。所以我认为next select不应该返回零,除非超时。检查这一点的方法是,当你得到一个意外的零返回时,迭代所有的就绪键,检查就绪操作,看看有什么。你怎么确定你已经处理了所有的就绪操作?当我得到意外的零时,没有就绪键。我使用了selector.selectedKeys(),但它似乎是一个空集:-(这可能真的是JDK中的一个bug。Netty和Mina似乎也遇到了这样的问题,他们将重建选择器作为解决方法。我遇到了这种行为(select()不断返回零)在android中,发现这是由于使用
interestTops==0
注册频道引起的,例如channel.register(选择器,0)
Nice well writed question+1您是否从此进程启动外部进程?@RoeeShenberg否。只有一个java进程而不调用其他进程。我最近遇到这样一个错误,原因是一个打开的文件,其中已注册的特定描述符已经关闭(例如,文件描述符被复制)(在我的例子中,这是由fork()引起的))。linux epoll接口以这种方式被破坏(尽管用于注册epoll的描述符已关闭,但该文件仍保持为epoll注册)。要进行调试,可以执行的操作是清除有问题的进程(筛选epoll\u wait
),然后查看返回的文件描述符,最后使用lsof
了解更多信息(例如,如果是套接字,目标是什么)
if (selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks() || hasScheduledTasks()) {
// - Selected something,
// - waken up by user, or
// - the task queue has a pending task.
// - a scheduled task is ready for processing
break;
}
...
} else if (SELECTOR_AUTO_REBUILD_THRESHOLD > 0 &&
selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) {
// The selector returned prematurely many times in a row.
// Rebuild the selector to work around the problem.
logger.warn(
"Selector.select() returned prematurely {} times in a row; rebuilding selector.",
selectCnt);
rebuildSelector();
selector = this.selector;
// Select again to populate selectedKeys.
selector.selectNow();
selectCnt = 1;
break;
}
if (!wakeupCalled.getAndSet(false) && (selected == 0) && (delta < 100)) {
// Last chance : the select() may have been
// interrupted because we have had an closed channel.
if (isBrokenConnection()) {
LOG.warn("Broken connection");
} else {
LOG.warn("Create a new selector. Selected is 0, delta = " + (t1 - t0));
// Ok, we are hit by the nasty epoll
// spinning.
// Basically, there is a race condition
// which causes a closing file descriptor not to be
// considered as available as a selected channel,
// but
// it stopped the select. The next time we will
// call select(), it will exit immediately for the
// same
// reason, and do so forever, consuming 100%
// CPU.
// We have to destroy the selector, and
// register all the socket on a new one.
registerNewSelector();
}
}