前面一篇中已经介绍了基本IO的使用以及最简单的阻塞服务器的例子,本篇就来介绍下NIO的相关内容,前面的分享可以参考目录:

  1. 网络IO的基本知识与概念
  2. 普通IO以及BIO服务器
  3. NIO的使用与服务器Hello world
  4. Netty的使用与服务器Hello world

NIO,也叫做new-IO或者non-blocking-IO,就暂且理解为非阻塞IO吧。

为什么选择NIO

那么NIO相对于IO来说,有什么优势呢?总结来说:

  1. IO是面向流的,数据只能从一端读取到另一端,不能随意读写。NIO则是面向缓冲区的,进行数据的操作更方便了
  2. IO是阻塞的,既浪费服务器的性能,也增加了服务器的风险;而NIO是非阻塞的。
  3. NIO引入了IO多路复用器,效率上更高效了。

NIO都有什么

那么NIO都提供了什么呢?

  1. 基于缓冲区的双向管道,Channel和Buffer
  2. IO多路复用器Selector
  3. 更为易用的API

Buffer的使用

在NIO中提供了各种不同的Buffer,最常用的就是ByteBuffer:

可以看到,他们都有几个比较重要的变量:

  • capacity——容量,这个值是一开始申请就确定好的。类似c语言申请数组的大小。
  • limit——剩余,在写模式下初始的时候等于capacity;在读模式下,等于最后一次写入的位置
  • mark——标记位,标记一下position的位置,可以调用reset()方法回到这个位置。
  • posistion——位置,写模式下表示开始写入的位置;读模式下表示开始读的位置

总结来说,NIO的Buffer有两种模式,读模式和写模式。刚上来就是写模式,使用flip()可以切换到读模式。

关于这几个位置的使用,可以参考下面的代码:

public class ByteBufferTest {
public static void main(String[] args) { ByteBuffer buffer = ByteBuffer.allocate(88);
System.out.println(buffer); String value = "Netty权威指南";
buffer.put(value.getBytes());
System.out.println(buffer); buffer.flip();
System.out.println(buffer); byte[] v = new byte[buffer.remaining()];
buffer.get(v); System.out.println(buffer);
System.out.println(new String(v));
}
}

得到的输出为:

java.nio.HeapByteBuffer[pos=0 lim=88 cap=88]
java.nio.HeapByteBuffer[pos=17 lim=88 cap=88]
java.nio.HeapByteBuffer[pos=0 lim=17 cap=88]
java.nio.HeapByteBuffer[pos=17 lim=17 cap=88]
Netty权威指南

读者可以自己领会一下,这几个变量的含义。另外说明一点,如果遇到自己定义POJO类,就可以像这里的Buffer重载toString()方法,这样输出的时候就很方便了。

最后关于ByteBuffer在Channel中的使用,可以参考下面的代码:

public class BufferTest {
public static void main(String[] args) throws IOException {
String file = "xxxx/test.txt";
RandomAccessFile accessFile = new RandomAccessFile(file,"rw");
FileChannel fileChannel = accessFile.getChannel();
// 20个字节
ByteBuffer buffer = ByteBuffer.allocate(20);
int bytesRead = fileChannel.read(buffer);
// buffer.put()也能写入buffer
while(bytesRead!=-1){
// 写切换到读
buffer.flip(); while(buffer.hasRemaining()){
System.out.println((char)buffer.get());
} // buffer.rewind()重新读
// buffer.mark()标记position buffer.reset()恢复 // 清除缓冲区
buffer.clear();
// buffer.compact(); 清楚读过的数据
bytesRead = fileChannel.read(buffer);
}
}
}

这样,就熟悉了Channel和ByteBuffer的使用。接下来,看看服务器中的应用吧。

NIO服务器例子

前面BIO的服务器,是来一个连接就创建一个新的线程响应。这里基于NIO的多路复用,可以这样写:


import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
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.Iterator;
import java.util.Set; public class PlainNioServer {
public void serve(int port) throws IOException { // 创建channel,并绑定监听端口
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
ServerSocket ssocket = serverSocketChannel.socket();
InetSocketAddress address = new InetSocketAddress(port);
ssocket.bind(address); //创建selector,并将channel注册到selector
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); final ByteBuffer msg = ByteBuffer.wrap("Hi\r\b".getBytes()); for(;;){
try{
selector.select(); }catch (IOException e){
e.printStackTrace();
break;
} Set<SelectionKey> readyKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = readyKeys.iterator(); while(iterator.hasNext()){
SelectionKey key = iterator.next();
iterator.remove(); try{
if(key.isAcceptable()){
ServerSocketChannel server = (ServerSocketChannel)key.channel();
SocketChannel client= server.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, msg.duplicate());
System.out.println("accepted connection from "+client);
} if(key.isWritable()){
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = (ByteBuffer) key.attachment();
while(buffer.hasRemaining()){
if(client.write(buffer)==0){
break;
}
}
client.close();
}
}catch (IOException e){
key.cancel();
try{
key.channel().close();
} catch (IOException ex){
ex.printStackTrace();
}
}
} }
} public static void main(String[] args) throws IOException {
PlainNioServer server = new PlainNioServer();
server.serve(5555);
}
}

这里抽象来说是下面的步骤:

  1. 创建ServerSocketChannel并绑定端口
  2. 创建Selector多路复用器,并注册Channel
  3. 循环监听是否有感兴趣的事件发生selector.select();
  4. 获得事件的句柄,并进行处理

其中Selector可以一次监听多个IO处理,效率就提高很多了。

漫谈Java IO之 NIO那些事儿的更多相关文章

  1. 如何解读 Java IO、NIO 中的同步阻塞与同步非阻塞?

    原文链接:如何解读 Java IO.NIO 中的同步阻塞与同步非阻塞? 一.前言 最近刚读完一本书:<Netty.Zookeeper.Redis 并发实战>,个人觉得 Netty 部分是写 ...

  2. 漫谈Java IO之 Netty与NIO服务器

    前面介绍了基本的网络模型以及IO与NIO,那么有了NIO来开发非阻塞服务器,大家就满足了吗?有了技术支持,就回去追求效率,因此就产生了很多NIO的框架对NIO进行封装--这就是大名鼎鼎的Netty. ...

  3. java IO和NIO 的区别

    Java NIO和IO的主要区别 下表总结了Java NIO和IO之间的主要差别. IO                NIO 面向流            面向缓冲 阻塞IO           非 ...

  4. Java IO 和 NIO

    昨天面试问到了有关Java NIO的问题,没有答上来.于是,在网上看到了一篇很有用的系列文章讲Java IO的,浅显易懂.后面的备注里有该系列文章的链接.内容不算很长,需要两个小时肯定看完了,将该系列 ...

  5. 漫谈Java IO之普通IO流与BIO服务器

    今天来复习一下基础IO,也就是最普通的IO. 网络IO的基本知识与概念 普通IO以及BIO服务器 NIO的使用与服务器Hello world Netty的使用与服务器Hello world 输入流与输 ...

  6. Java IO、NIO、AIO知识总结

    本文主要讲述下自己对IO的理解,对IO的用法和细则可能没有顾虑到. 本文的理解基于以下几篇文章,他们对各自部分都讲的很细,对我理解IO提供了很大帮助. https://www.cnblogs.com/ ...

  7. java IO和NIO区别

    面向流与面向缓冲 Java NIO和IO之间第一个最大的区别是,IO是面向流的,NIO是面向缓冲区的. Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方 ...

  8. 关于Java IO与NIO知识都在这里

    由于内容比较多,我下面放的一部分是我更新在我的微信公众号上的链接,微信排版比较好看,更加利于阅读.每一篇文章下面我都把文章的主要内容给列出来了,便于大家学习与回顾. Java面试通关手册(Java学习 ...

  9. java IO、NIO、AIO详解

    概述 在我们学习Java的IO流之前,我们都要了解几个关键词 同步与异步(synchronous/asynchronous):同步是一种可靠的有序运行机制,当我们进行同步操作时,后续的任务是等待当前调 ...

随机推荐

  1. dojo中获取表格中某一行的某个值

    dojo中经常出现对表格中的某行进行操作,如单击某行修改.删除等.那怎样获取某行的唯一标示呢? 如查询表格中的某列有个userId,并且这个是唯一的,那么可以通过它来访问这一列 具体操作代码如下: v ...

  2. 事件绑定与解除js

    //Ys为元素,Sj为事件,Hs为函数,bol选择是true绑定事件,还是false解除事件 //需要提前定义a,b,c,bol function thing(Ys,Sj,Hs,bol){ var i ...

  3. Good Bye 2017 D. New Year and Arbitrary Arrangement

    看了别人的题解 首先这题是一个dp dp[i][j] i是当前有多少个a j是当前有多少个ab子序列 dp[i][j] = dp[i+1][j]*Pa + dp[i][i+j]*Pb; i,j 时加一 ...

  4. 异常---ment.getElementById("searchForm").submit is not a function

    今天在写代码的时候JS一直报上面这个错.搞了半天一直想不明白 .我看别的页面都是这样写了就是没有一点错.. 可能是写了一个晚上的代码..头有点晕..后来终于找到原因了..浪费我两个小时啊..杯具.. ...

  5. 在vue中使用css modules替代scroped

    前面的话 css modules是一种流行的模块化和组合CSS的系统. vue-loader提供了与css modules的集成,作为scope CSS的替代方案.本文将详细介绍css modules ...

  6. SOA和微服务架构

    微服务架构强调的第一个重点就是业务系统需要彻底的组件化和服务化,原有的单个业务系统会拆分为多个可以独立开发,设计,运行和运维的小应用.这些小应用之间通过服务完成交互和集成.每个小应用从前端web ui ...

  7. 从输入URL到页面加载的过程?如何由一道题完善自己的前端知识体系!

    前言 见解有限,如有描述不当之处,请帮忙指出,如有错误,会及时修正. 为什么要梳理这篇文章? 最近恰好被问到这方面的问题,尝试整理后发现,这道题的覆盖面可以非常广,很适合作为一道承载知识体系的题目. ...

  8. CF922 CodeForces Round #461(Div.2)

    CF922 CodeForces Round #461(Div.2) 这场比赛很晚呀 果断滚去睡了 现在来做一下 A CF922 A 翻译: 一开始有一个初始版本的玩具 每次有两种操作: 放一个初始版 ...

  9. Poj3321 Apple tree

    翻译: 卡卡屋前有一株苹果树,每年秋天,树上长了许多苹果.卡卡很喜欢苹果.树上有N个节点,卡卡给他们编号1到N,根的编号永远是1.每个节点上最多结一个苹果.卡卡想要了解某一个子树上一共结了多少苹果. ...

  10. gulp详细入门

    gulp是基于Nodejs的自动任务运行器, 她能自动化地完成 javascript/coffee/sass/less/html/image/css 等文件的的测试.检查.合并.压缩.格式化.浏览器自 ...