博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JavaNIO基本使用以及性能问题分析
阅读量:2489 次
发布时间:2019-05-11

本文共 4100 字,大约阅读时间需要 13 分钟。

什么是NIO?

NIO在操作系统当前一般定义为:non-blocking io,JDK1.4开始新引入了NIO相关的类库,所以有时候人们在Java中有叫他:new io。

当你使用NIO请求时,等待客户端请求,或者等待数据的到达都不会阻塞了,而是直接返回给你一个错误信息。

简单来说就是如果socket标记为阻塞,那么accept调用就会阻塞,直接有连接到达,如果socket标记为非阻塞,并且也没有连接到达,那么就会返回一个EAGAIN或EWOULDBLOCK类型的错误。

在这里插入图片描述

代码演示

服务端代码

import java.net.InetSocketAddress;import java.nio.ByteBuffer;import java.nio.channels.ServerSocketChannel;import java.nio.channels.SocketChannel;import java.util.ArrayList;import java.util.Iterator;import java.util.List;public class SocketNIO {
public static void main(String[] args) throws Exception {
//保存所有已经连接进来的客户端 List
socketChannelList = new ArrayList<>(); ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress(9090)); serverSocketChannel.configureBlocking(false);//一定要设置为非阻塞模式 System.out.println("
<服务端启动,并且设置为非阻塞模式!>
"); while (true) {
SocketChannel accept = serverSocketChannel.accept(); if (accept == null) {
Thread.sleep(5000);//这里纯粹为了演示效果,实际情况下可以不需要休眠,直接快速处理后面的read事件 System.out.println("accept 没有阻塞,而是返回了null!"); } else {
//如果有请求到达时,设置为后续事件为非阻塞模式 System.out.println("---接受客户端连接请求,并设置为非阻塞模式!---"); accept.configureBlocking(false);//一定要设置为非阻塞模式 socketChannelList.add(accept);//添加集合中 } ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024); Iterator
iterator = socketChannelList.iterator(); while (iterator.hasNext()) {
SocketChannel sc = iterator.next(); //遍历所有客户端,看看是否有数据到达,有则read大于0,没有也不会阻塞了。 try {
int read = sc.read(byteBuffer); if (read > 0) {
byteBuffer.flip(); byte[] bytes = new byte[byteBuffer.limit()]; byteBuffer.get(bytes); System.out.println("接收到客户端:" + sc.socket().getPort() + ",发送的数据:" + new String(bytes)); byteBuffer.clear(); } else {
System.out.println("客户端没有数据到达。。。"); } } catch (Exception e) {
System.out.println("---客户端下线---"); byteBuffer.clear(); iterator.remove(); } } } }}

客户端代码

import java.io.*;import java.net.Socket;public class SocketCli {
public static void main(String[] args) throws Exception {
Socket socket = new Socket("localhost", 9090); System.out.println("客户端启动..."); OutputStream outputStream = socket.getOutputStream(); BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream)); BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); while (true) {
String readLine = reader.readLine(); if (readLine.equals("quit")) {
bufferedWriter.write(readLine); bufferedWriter.newLine(); bufferedWriter.flush(); System.exit(-1); } else {
bufferedWriter.write(readLine); bufferedWriter.newLine(); bufferedWriter.flush(); } } }}

代码很简单,通过nio提供的api,可以简单的实现一个服务端单线程下不阻塞的处理多个客户端的请求。

BIO与NIO比较

BIO模型

在这里插入图片描述

NIO模型

在这里插入图片描述

两个模型的区别,显而易见,在等待数据到达时BIO是阻塞的,直到数据到达,NIO是通过不断的询问内核,判断是否有数据到达,询问的频率由自己控制,刚才演示的代码中我就设置的每5秒询问一次。

无论是BIO还是NIO,都需要用户主动调用recvfrom函数来询问内核数据是否到达,到达以后再等数据从内核拷贝到用户空间,这种模型就是同步的,只不过BIO是同步阻塞IO,而NIO是同步非阻塞IO。

Linux中的系统调用

1、先启动服务端,服务端调用accept函数后,并没有阻塞而是直接返回了EAGAIN,和上面的分析的结果一致。

在这里插入图片描述

2、当客户端启动之后,再看服务端调用,accept得到了返回信息,并且开始调用read函数,客户端没有发送任何数据时,read函数也是返回EAGAIN,并没有阻塞。

在这里插入图片描述

3、客户端向服务端发送数据。

客户端发送:11111

在这里插入图片描述

服务端收到

在这里插入图片描述

在这里插入图片描述

NIO的问题分析

通过以上的分析,很容易看出以下两点问题:

  • 服务端需要记录所有连接进来的客户端,然后挨个遍历每一个客户端的请求,时间复杂度O(n)。
  • 无论是否真的有数据到来,都需要调用read,实际上就意味着会发生一次系统调用(用户态与内核态切换),而如果服务端接收了1W个客户端,但只有1个有请求到来,那么就意味着其中9999都是无效的请求。

基于上面的问题,就延伸出了一种的新的模型:多路复用IO模型,当然只有epoll真正解决了这两个问题,之后将另一些一遍文章再分析一下多路复用IO模型。

转载地址:http://iolrb.baihongyu.com/

你可能感兴趣的文章
test
查看>>
12.2号
查看>>
等比数列前N项和的公式推导
查看>>
windows系统查找文件-通配符的使用
查看>>
python爬虫:其他操作
查看>>
IDEA 运行spingboot时出现Process finished with exit code -1073741819 (0xC0000005)
查看>>
大数据学习之Linux环境搭建(导航)
查看>>
[企业化NET]Window Server 2008 R2[3]-SVN 服务端 和 客户端 基本使用
查看>>
模板方法模式
查看>>
word20161207
查看>>
【转】Java MySQL数据类型对照
查看>>
poj 3070
查看>>
javascript:正则表达式对象
查看>>
esper(4-3)-Non-Overlapping Context
查看>>
python web django base skill
查看>>
arcgis建立拓扑分析(检验矢量图)
查看>>
SQL Server 中创建数据库、更改主文件组示例
查看>>
Ubuntu下修改file descriptor
查看>>
全排列问题(递归)
查看>>
题目1179:阶乘-------------阶乘不用long long int 就不能AC
查看>>