在我当前的项目中,我注意到 select()
没有像预期的那样阻塞。
它根本不阻塞并总是返回,即使没有 IO 存在。所以我的 CPU 很忙。
注册总是会被另一个线程调用,所以我需要锁和唤醒。
文档对 selectNow()
说:
Invoking this method clears the effect of any previous invocations of the wakeup method.
所以我在每次迭代结束时调用该方法。没有成功。
我没有找到关于如何使用 selectNow
的示例或解释。
代码有什么问题?
这是我的示例代码,因此您可以对其进行测试。
顺便说一句:另一个 stackoverflow 问题是我的代码的角色模型。 编辑:示例已修复!现在可以使用了。
import java.io.IOException;
import java.net.*;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.concurrent.locks.ReentrantLock;
public class Test implements Runnable {
ReentrantLock selectorLock = new ReentrantLock();
Selector selector;
boolean alive;
@Override
public void run() {
SelectionKey key;
Iterator<SelectionKey> keys;
alive = true;
try {
while (alive) {
selectorLock.lock();
selectorLock.unlock();
selector.select();
System.out.println("select() returned");
keys = selector.selectedKeys().iterator();
// handle each "event"
while (keys.hasNext()) {
key = keys.next();
// mark as handled
keys.remove();
// handle
handleKey(key);
}
//selector.selectNow(); // don't fix this
}
} catch ( IOException e ) {
e.printStackTrace();
}
}
private void handleKey(SelectionKey key)
throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
if (key.isConnectable()) {
System.out.println("connecting");
if ( channel.finishConnect() ) {
key.interestOps(SelectionKey.OP_READ);
} else {
key.cancel();
}
} else if (key.isReadable()) {
System.out.println("reading");
// read and detect remote close
channel.read(ByteBuffer.allocate(64));
}
}
public void register(SelectableChannel channel, int ops, Object attachment)
throws ClosedChannelException {
selectorLock.lock();
try {
System.out.println("wakeup");
selector.wakeup();
channel.register(selector, ops, attachment);
} finally {
selectorLock.unlock();
}
}
public Test()
throws IOException {
selector = Selector.open();
}
public static void main(String[] args)
throws IOException {
Test t = new Test();
new Thread(t).start();
SocketAddress address = new InetSocketAddress("localhost", 8080);
SocketChannel channel = SocketChannel.open();
channel.configureBlocking(false);
channel.connect(address);
t.register(channel, SelectionKey.OP_CONNECT, "test channel attachment");
}
}
最佳答案
在OP_CONNECT
触发并且 finishConnect()
返回“true”之前,不要注册OP_READ
。此时您必须注销 OP_CONNECT
。
同样,在您有东西要写之前,不要为 OP_WRITE 注册 channel 。 OP_WRITE
总是准备就绪,除非套接字发送缓冲区已满,因此它应该只在 检测到该情况后注册 (write()
返回零),你应该在它触发时立即注销它(除非这种情况再次发生)。
最后 OP_CONNECT
和 OP_WRITE
是相同的东西,鉴于我刚才所说的 OP_WRITE
解释了你的选择器旋转。
关于java - Selector.select 没有按预期阻塞,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7249421/