关于JAVA NIO死循环的研究
NIO写事件比较特殊,触发的条件取决于写缓冲区是否有空闲,不然会进入死循环。
直接上代码
服务端:
public static void main(String[] args) {
try {
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.bind(new InetSocketAddress("127.0.0.1", 9999));
serverChannel.configureBlocking(false);
Selector sel = Selector.open();
serverChannel.register(sel, SelectionKey.OP_ACCEPT);
while(true){
sel.select();
Set<SelectionKey> set = sel.selectedKeys();
System.out.println(set.size());
Iterator<SelectionKey> iterator = set.iterator();
while(iterator.hasNext()){
SelectionKey key = (SelectionKey)iterator.next();
iterator.remove();
handler(key);
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void handler(SelectionKey key){
SocketChannel client;
Selector sel = key.selector();
try {
if(key.isAcceptable()){
ServerSocketChannel ser = (ServerSocketChannel)key.channel();
client = ser.accept();
client.configureBlocking(false);
client.register(sel,SelectionKey.OP_READ);
}else if(key.isReadable()){
SocketChannel se = (SocketChannel)key.channel();
ByteBuffer ch = ByteBuffer.allocate(1024);
se.read(ch);
System.out.println(new String(ch.array()));
ByteBuffer by = ByteBuffer.wrap(new String("我是服务器").getBytes());
se.write(by);
se.register(sel, SelectionKey.OP_WRITE);
}else if(key.isWritable()){
SocketChannel ch = (SocketChannel)key.channel();
ByteBuffer by = ByteBuffer.wrap(new String("我是服务器").getBytes());
ch.write(by);
}
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
客户端:
public static void main(String[] args) {
SocketChannel client;
try {
client = SocketChannel.open();
Selector sel = Selector.open();
client.configureBlocking(false);
client.register(sel,SelectionKey.OP_CONNECT);
//并不正在建立连接,直到调用client.finishConnect();
client.connect(new InetSocketAddress("127.0.0.1", 9999));
while(true){
sel.select();
Iterator<SelectionKey> it = sel.selectedKeys().iterator();
while(it.hasNext()){
SelectionKey key = (SelectionKey)it.next();
handler(key);
it.remove();
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void handler(SelectionKey key){
if(key.isConnectable()){
SocketChannel client = (SocketChannel)key.channel();
Selector sel = key.selector();
//连接就绪
if(client.isConnectionPending()){
try {
//真正建立连接
client.finishConnect();
client.configureBlocking(false);//非阻塞
client.write(ByteBuffer.wrap(new String("我是客户端").getBytes()));
client.register(sel, SelectionKey.OP_READ);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//读就绪
}else if(key.isReadable()){
read(key);
}
}
public static void read(SelectionKey key){
SocketChannel cl = (SocketChannel)key.channel();
ByteBuffer by = ByteBuffer.allocate(1024);
try {
cl.read(by);
System.out.println(new String(by.array()));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
上诉代码会进入死循环,原因是写事件key.isWritable()
写事件相对读事件比较特殊,一般来说,你最好不要注册写事件。写事件的就绪条件为底层缓冲区有空闲空间,而写缓冲区绝大部分时间都是有空闲空间的,所以当你注册写事件后,写操作一直是就绪的,selector会一直搜索到该事件(写操作),从而一直占用整个CPU资源,进入死循环。所以,只有当你确实有数据要写时再注册写操作,并在写完以后马上取消注册。一般不建议注册写事件。
写博客事件不长,有什么不正确请及时@我,谢谢。