Java NIO非阻塞模式
当线程从某通道读写数据时,若没有数据可用时,该线程可以进行其他任务。即线程空闲时可以进行其他任务,单独的线程可以管理多个输入输出通道,大大提高了CPU使用率。
选择器(Selector)
选择器可以让一个线程控制多个通道。Selector是非阻塞IO的核心。
选择器的使用
- 创建选择器
//获取选择器
Selector selector = Selector.open();
- 向选择器注册通道
//将通道注册到选择器上, 并且指定监听事件
ssChannel.register(selector, SelectionKey.OP_ACCEPT);
//当调用register(Selector sel, int ops)将通道注册选择器时,选择器对通道的监听事件,通过第二个参数ops指定
可监听的事件类型(可用SelectionKey的四个常量指定)
读 : SelectionKey.OP_READ (1)
写 : SelectionKey.OP_WRITE (4)
连接 : SelectionKey.OP_CONNECT(8)
接收 : SelectionKey.OP_ACCEPT (16)
若注册的事件不止一个可以用 | 操作符连接
具体代码展示(带注释)
package com.ht.IO;
import org.junit.Test;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Date;
import java.util.Iterator;
/**
* @author ht
* @create 2021-02-06 13:59
* @description
*/
public class NonBlockingNIO {
@Test
public void client() {
SocketChannel sChannel = null;
try {
sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 8989));
//切换非阻塞模式
sChannel.configureBlocking(false);
ByteBuffer buf = ByteBuffer.allocate(1024);
buf.put(new Date().toString().getBytes());
buf.flip();
sChannel.write(buf);
buf.clear();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (sChannel != null){
try {
sChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Test
public void server(){
try {
ServerSocketChannel ssChannel = ServerSocketChannel.open();
ssChannel.configureBlocking(false);
ssChannel.bind(new InetSocketAddress(8989));
//获取选择器
Selector selector = Selector.open();
//将通道注册到选择器上, 并且指定监听事件
ssChannel.register(selector, SelectionKey.OP_ACCEPT);
//轮询式获取选择器上已经“准备就绪”的事件
while(selector.select() > 0){
//获取当前选择器中所有注册的“选择键”(已注册的监听事件)
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while(iterator.hasNext()){
//获取准备就绪的事件
SelectionKey sk = iterator.next();
//判断具体是什么事件准备就绪
if(sk.isAcceptable()){
//若“接收就绪”,获取客户端连接
SocketChannel sChannel = ssChannel.accept();
//切换非阻塞模式
sChannel.configureBlocking(false);
//将该通道注册到选择器上
sChannel.register(selector, SelectionKey.OP_READ);
} else if(sk.isReadable()){
//获取当前选择器上“读就绪”状态的通道
SocketChannel socketChannel = (SocketChannel) sk.channel();
//读取数据
ByteBuffer buf = ByteBuffer.allocate(1024);
int len;
while((len = socketChannel.read(buf)) > 0){
buf.flip();
System.out.println(new String(buf.array(), 0, len));
buf.clear();
}
}
//取消选择键
iterator.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}