IO,NIO对比
传统的IO流都是阻塞式的。也就是,当一个线程调用read()和write()方法时,该线程被阻塞,直到有一些数据被读取或者写入,该线程在此期间不能执行其他任务。因此,在完成网络通信进行IO操作时,由于线程会阻塞,所以服务器端必须为每个客户端都提供一个独立的线程进行处理,当服务器端需要处理大量客户端时,性能急剧下降。
nio是非阻塞模式的。当线程从某通道进行读写数据时,若没有数据可用时,该线程可以进行其他任务。线程通常将非阻塞IO的空闲时间用于在其他通道上执行Io操作,所以单独的线程可以管理多个输入和输出通道。因此,nio可以让服务器端使用一个或者有限的几个线程来同时处理连接到服务器的所有客户端。
nio非阻塞模式举个栗子:
假如今天有一个快递要送到,快递具体什么时候通知也不知道。
io模式就是我今天必须在家等着,什么快递送到了,我领了快递之后才能干其他事。
nio模式就是我在家干其他事,而不必等待快递什么什么时候到,什么到我什么取。这时候我可以玩游戏,做饭等其他操作。
而Selector就是完成这个功能的核心。
NIO核心Selector选择器
Selector(选择器)是SelectableChannel对象的多路复用器,Selector可以同时监控多个SelectableChannel的IO情况,也就是说,利用Selector可使一个单独的线程管理多个Channel。Selector是非阻塞IO的核心。
SelectorChannel的结构图如下图:
选择器Selector的使用
1.创建Selector:通过调用Selector.open()方法创建一个selector。
2.向选择器注册通道:SekectableChannel。register(Selector sel,int ops)
@Test
public void testSelector() throws IOException {
//向选择器注册通道
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 9898);
//获取socketChannel
SocketChannel channel = socket.getChannel();
//创建选择器
Selector selector = Selector.open();
//将SocketChannel切换到非阻塞模式
channel.configureBlocking(false);
//向Selector注册
SelectionKey register = channel.register(selector, SelectionKey.OP_READ);
}
3.当调用register(Selector sel ,int ops)将通道注册选择器时,选择器对通道的监听事件,需要通过第二个参数ops指定。
4.可以监听的事件类型SelectionKey的四个常来量
读 : SelectionKey.OP_READ (1)
写 : SelectionKey.OP_WRITE (4)
连接 : SelectionKey.OP_CONNECT (8)
接收 : SelectionKey.OP_ACCEPT (16)
如果监听多个事件使用符号|表示
5.SelectionKey:表示SelectableChannel和Selector之间的注册关系。每次向选择器注册通道时就会选择一个事件(选择键)。选择键包含两个表示为整数的操作集。操作集的每一位都表示该建的通道所支持的一类可选操作。
SocketChannel
是一个连接到TCP网络套接字的通道
操作步骤:
打开socketChannel通道
读写数据
关闭SocketChannel
这个通道可以监听新进来的TCP连接的通道,就像标准IO的ServerSocket一样。
DatagramChannel
能收发UDP包的通道。
操作步骤:
打开DatagramChannel
接收、发送数据
管道(Pipe)
2个线程之间的单向数据连接。
Pipe有一个source通道和一个sink通道。数据会被写到sink通道,从source通道读取。
xia
向管道写数据
String str = "测试数据";
//创建管道
Pipe pipe = Pipe.open();
//向管道写输入
Pipe.SinkChannel sink = pipe.sink();
//通过 SinkChannel的write()方法写数据
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.clear();
buffer.put(str.getBytes());
buffer.flip();
while (buffer.hasRemaining()){
sink.write(buffer);
}
向管道读取数据