我们知道,系统运行的性能瓶颈通常在I/O读写,包括对端口和文件的操作上,过去,在打开一个I/O通道后,read()将一直等待在端口一边读取字节内容,如果没有内容进来,read()也是傻傻的等,这会影响我们程序继续做其他事情,那么改进做法就是开设线程,让线程去等待,但是这样做也是相当耗费资源的。

一、BIO带来的挑战

BIO即阻塞I/O,不管是磁盘I/O还是网络I/O,数据在写入OutputStream或者从InputStream读取时都有可能会阻塞,一旦阻塞,线程将会失去CPU的使用权,这在当前的大规模访问量和有性能要求的情况下是不能被接收的。虽然当前的网络I/O有一些解决办法,如一个客户端一个处理线程,出现阻塞时只是一个线程阻塞而不会影响其他线程工作,还有为了减少系统线程的开销,采用线程池的办法来减少线程创建和回收的成本,但是有一些使用场景下仍然是无法解决的。如当前的一些需要大量HTTP长连接的情况。即使线程的数量不是问题,仍然有一些问题是无法避免的,比如我们想给某些客户端更高的服务优先级,很难通过设计线程的优先级来完成。

二、NIO的工作方式

下图为NIO的相关类图:

其中有两个关键类:Channel和Selector,它们是NIO中的核心概念。

Channel:通道

Channel是一个对象,可以通过它读取和写入数据。拿 NIO 与原来的 I/O 做个比较,通道就像是流,而且他们面向缓冲区的。
正如前面提到的,所有数据都通过 Buffer 对象来处理。您永远不会将字节直接写入通道中,相反,您是将数据写入包含一个或者多个字节的缓冲区。同样,您不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。
通道与流的不同之处在于通道是双向的。而流只是在一个方向上移动(一个流必须是 InputStream 或者 OutputStream 的子类), 而 通道 可以用于读、写或者同时用于读写。
因为它们是双向的,所以通道可以比流更好地反映底层操作系统的真实情况。特别是在 UNIX 模型中,底层操作系统通道是双向的。

Selector:这个类似一个观察者,只要我们把需要探知的socketchannel告诉Selector,我们接着做别的事情,当有事件发生时,他会通知我们,传回一组SelectionKey,我们读取这些Key,就会获得我们刚刚注册过的socketchannel,然后,我们从这个Channel中读取数据,放心,包准能够读到,接着我们可以处理这些数据。

Selector内部原理实际是在做一个对所注册的channel的轮询访问,不断的轮询(目前就这一个算法),一旦轮询到一个channel有所注册的事情发生,比如数据来了,他就会站起来报告,交出一把钥匙,让我们通过这把钥匙来读取这个channel的内容。

三、基于NIO的Socket请求的处理过程

四、代码示例

public void selector() throws IOException {
ByteBuffer buffer = ByteBuffer.allocate(1024);
Selector selector = Selector.open();
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);//设置为非阻塞方式
ssc.socket().bind(new InetSocketAddress(8080));
ssc.register(selector,SelectionKey.OP_ACCEPT);//注册监听事件
while (true){
Set selectedKeys = selector.selectedKeys();//取得所有KEY集合
Iterator it = selectedKeys.iterator();
while(it.hasNext()){
SelectionKey key = (SelectionKey) it.next();
if((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT){
ServerSocketChannel ssChannel = (ServerSocketChannel)key.channel();
SocketChannel sc = ssChannel.accept();//接收到服务端的请求
sc.configureBlocking(false);
sc.register(selector,SelectionKey.OP_READ);
it.remove();
}else if((key.readyOps() & SelectionKey.OP_READ)==SelectionKey.OP_READ){
SocketChannel sc = (SocketChannel)key.channel();
while(true){
buffer.clear();
int n = sc.read(buffer);//读取数据
if(n<=0){
break;
}
buffer.flip();
}
it.remove();
}
}
}
}

转载于:https://blog.51cto.com/yuke198907/1340766

I/O读写的另一种方式-NIO相关推荐

  1. 最近总结了串口(COM)读写操作的三种方式

    最近总结了串口(COM)读写操作的三种方式: 第1种方式是采用微软在.NET2.0推出了一个串口控件,SerialPort类,但必须是.NET2.0才可以 第2种方式是用API写串口通信,虽然难度高, ...

  2. Spark读写Hbase的二种方式对比

    作者:Syn良子 出处:http://www.cnblogs.com/cssdongl 转载请注明出处 一.传统方式 这种方式就是常用的TableInputFormat和TableOutputForm ...

  3. QT读写Sqlite数据库三种方式

    QT对一些基本的数据库的访问封装,可谓是极大的方便的我们开发人员,现在我们就来说下QT对Sqlite这个数据库的读写,Sqlite是一个比较小型的本地数据库,对于保存一些软件配置参数或量不是很大的数据 ...

  4. spark sql hbase java_Spark 读写 HBase 的两种方式(RDD、DataFrame)

    使用 saveAsHadoopDataset 写入数据 import org.apache.hadoop.hbase.{HBaseConfiguration, HTableDescriptor, Ta ...

  5. golang 读写文件的四种方式

    读文件 读取的文件放在file/test:也就是file包下的test这个文件,里面写多一点文件 读文件方式一:利用ioutil.ReadFile直接从文件读取到[]byte中 func Read0( ...

  6. 用CSV文件读写数据的两种方式(转)

    导读:有时候我们需要对收集的数据做统计,并在页面提供显示以及下载.除了对传统的excel存取之外,对CSV文件的存取也很重要.本文列出了这两种操作的详细代码. 代码: <?php $file = ...

  7. Qt读写文件(2种方式)实现详解

    文件的读写是很多应用程序具有的功能,甚至某些应用程序就是围绕着某一种格式文件的处 理而开发的,所以文件读写是应用程序开发的一个基本功能. 文本文件是指以纯文本格式存储的文件,例如用 Qt Creato ...

  8. Java读写文件的几种方式

    前言 Java中读写文件是非常基本的IO操作了,现在总结一下常见的用法.首先总结一下读取文件的步骤: 根据文件的路径获取到文件File对象 将File对象转换成输入流InputStream 将输入流读 ...

  9. golang读写文件的几种方式

    golang中处理文件有很多种方式,下面我们来看看. (1)使用os模块 先来看看如何查看文件属性 package mainimport ("fmt""os" ...

最新文章

  1. 大规模业务服务器开发总结
  2. 《Java入门经典(第7版)》—— 6.11 练习
  3. Windows2003 IIS6.0启用Gzip功能
  4. 如何用vue-router为每个路由配置各自的title
  5. c语言数组在栈上的分配,彻底弄懂为什么不能把栈上分配的数组(字符串)作为返回值...
  6. Web服务(Apache、Nginx、Tomcat、Jetty)与应用(LAMP、CMS-WordPressGhost、Jenkins、Gitlab)
  7. 带有Flask的服务器端DataTable
  8. java文本区水平对齐方式,如何将文本居中在水平StackLayout中?
  9. 全网最细JAVA窗口背景图片设置
  10. JavaWeb在线聊天系统开发
  11. ENVI执行监督分类后分类结果背景也被分类了的解决方案
  12. NEO主要技术社区成员大曝光
  13. 第7章,广义相加模型(GAMs)
  14. 基于pam实现的批量执行命令工具-Cyberark
  15. CityEngine2018正版免费申请试用教程
  16. 为何一些人认为从事 IT 行业的人是屌丝男?
  17. 2345加速浏览器有哪些特点
  18. 基于S32DS实现CAN、LIN基础结合芯片UJA1075的功耗模式切换设置(Standby、Normal、Sleep)
  19. 转:C语言面试题大汇总 (图像处理方向)
  20. 【Basic Use Case】

热门文章

  1. 8.1 Ext JS应用测试概览
  2. 网页爬虫,HttpClient+Jericho HTML Parser 实现网页的抓取
  3. linux java gc_Java GC机制及相关
  4. sql如何取前几行_重磅!蚂蚁金服开源机器学习工具SQLFlow,机器学习比SQL还简单...
  5. centos7 安装 mysql8 强制修改密码
  6. Spring Cloud基础入门
  7. 用python画的基本知识_Opencv-python画图基础知识
  8. java 创建bean_java – 使用spring按需创建bean
  9. mysql数据库一列多值查询
  10. git分支添加访问权限