1.NIO初识

反应器模式

  使用单线程模拟多线程,提高资源利用率和程序的效率,增加系统吞吐量。下面例子比较形象的说明了什么是反应器模式:

  一个老板经营一个饭店,

  传统模式 - 来一个客人安排一个服务员招呼,客人很满意;(相当于一个连接一个线程)

  后来客人越来越多,需要的服务员越来越多,资源条件不足以再请更多的服务员了,传统模式已经不能满足需求。老板之所以为老板自然有过人之处,老板发现,服务员在为客人服务时,当客人点菜的时候,服务员基本处于等待状态,(阻塞线程,不做事)。

  于是乎就让服务员在客人点菜的时候,去为其他客人服务,当客人菜点好后再招呼服务员即可。 --反应器(reactor)模式诞生了

  饭店的生意红红火火,几个服务员就足以支撑大量的客流量,老板用有限的资源赚了更多的money~~~~_

 通道:类似于流,但是可以异步读写数据(流只能同步读写),通道是双向的,(流是单向的),通道的数据总是要先读到一个buffer 或者 从一个buffer写入,即通道与buffer进行数据交互。

  

通道类型:  

  • FileChannel:从文件中读写数据。  

  • DatagramChannel:能通过UDP读写网络中的数据。  

  • SocketChannel:能通过TCP读写网络中的数据。  

  • ServerSocketChannel:可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。  

  • FileChannel比较特殊,它可以与通道进行数据交互, 不能切换到非阻塞模式,套接字通道可以切换到非阻塞模式;

缓冲区 - 本质上是一块可以存储数据的内存,被封装成了buffer对象而已!

 

缓冲区类型:

  • ByteBuffer  
  • MappedByteBuffer  
  • CharBuffer  
  • DoubleBuffer  
  • FloatBuffer  
  • IntBuffer  
  • LongBuffer  
  • ShortBuffer  

     

常用方法:

  • allocate() - 分配一块缓冲区  
  • put() - 向缓冲区写数据
  • get() - 向缓冲区读数据  
  • filp() - 将缓冲区从写模式切换到读模式  
  • clear() - 从读模式切换到写模式,不会清空数据,但后续写数据会覆盖原来的数据,即使有部分数据没有读,也会被遗忘;  
  • compact() - 从读数据切换到写模式,数据不会被清空,会将所有未读的数据copy到缓冲区头部,后续写数据不会覆盖,而是在这些数据之后写数据
  • mark() - 对position做出标记,配合reset使用
  • reset() - 将position置为标记值    

 

缓冲区的一些属性:

  • capacity - 缓冲区大小,无论是读模式还是写模式,此属性值不会变;

  • position - 写数据时,position表示当前写的位置,每写一个数据,会向下移动一个数据单元,初始为0;最大为capacity - 1,切换到读模式时,position会被置为0,表示当前读的位置

  • limit - 写模式下,limit 相当于capacity 表示最多可以写多少数据,切换到读模式时,limit 等于原先的position,表示最多可以读多少数据。

非直接缓冲区:通过allocate() 方法 分配缓冲区,将缓冲区建立在JVM内存中

直接缓冲区:通过allocateDirect() 方法直接缓冲区 将缓冲区建立在物理内存中

 

2. NIO实战

2.1 关于缓冲区各个属性的测试

	    String str = "abcde";

		//1. 分配一个指定大小的缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024); System.out.println("--------------allocate()----------------");
System.out.println(buf.position());//0
System.out.println(buf.limit());//1024
System.out.println(buf.capacity());//1024 //2. 利用put存入数据到缓冲区中去
buf.put(str.getBytes()); System.out.println("----------------put()-------------------");
System.out.println(buf.position());//5
System.out.println(buf.limit());//1024
System.out.println(buf.capacity());//1024 //3. 切换到读取模式
buf.flip(); System.out.println("----------------flip()------------------");
System.out.println(buf.position());//0
System.out.println(buf.limit());//5
System.out.println(buf.capacity());//1024 //4. 利用get() 读取缓冲区中的数据
byte[] dst = new byte[buf.limit()];
buf.get(dst);
System.out.println(new String(dst,0,dst.length)); System.out.println("----------------get()------------------");
System.out.println(buf.position());//5
System.out.println(buf.limit());//5
System.out.println(buf.capacity());//1024 //5.可重复读
buf.rewind(); System.out.println("----------------rewind()------------------");
System.out.println(buf.position());//0
System.out.println(buf.limit());//5
System.out.println(buf.capacity());//1024 //6.clear(): 清空缓冲区, 但是缓冲区的数据依然存在, 但是处于被遗忘的状态
buf.clear(); System.out.println("----------------clear()-------------------");
System.out.println(buf.position());//0
System.out.println(buf.limit());//1024
System.out.println(buf.capacity());//1024 byte[] newByte = new byte[buf.limit()];
buf.get(newByte);
System.out.println(new String(newByte,0,newByte.length));

 

2.2 关于通道的使用

2.2.1利用通道进行文件的复制(非直接缓冲区)


FileInputStream fis = null;
FileOutputStream fos = null;
FileChannel inChannel = null;
FileChannel outChannel = null;
try {
fis = new FileInputStream("1.jpg");
fos = new FileOutputStream("2.jpg"); // ①获取通道
inChannel = fis.getChannel();
outChannel = fos.getChannel(); // ②将通道中的数据存入缓冲区
ByteBuffer byteBuffer = ByteBuffer.allocate(1024); // 将通道中的数据存入缓冲区
while (inChannel.read(byteBuffer) != -1) {
byteBuffer.flip(); // 切换读取数据的模式
outChannel.write(byteBuffer);
byteBuffer.clear();
} } catch (IOException e) {
e.printStackTrace();
} finally {
if (inChannel != null) {
try {
inChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
} if (outChannel != null) {
try {
outChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
} if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
} if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
} }

 

2.2.2 通道之间的传输

CREATE_NEW:如果文件不存在就创建,存在就报错

CREATE:如果文件不存在就创建,存在创建(覆盖)


FileChannel inChannel = null;
FileChannel outChannel = null;
try {
inChannel = FileChannel.open(Paths.get("hello.txt"), StandardOpenOption.READ);
outChannel = FileChannel.open(Paths.get("hello2.txt"), StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE_NEW); inChannel.transferTo(0, inChannel.size(), outChannel);
} catch (Exception e) {
e.printStackTrace();
} finally { if(inChannel != null){
try {
inChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
} if(outChannel != null){
try {
outChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

 

2.2.3 内存文件的复制(直接缓冲区)

		FileChannel inChannel = null;
FileChannel outChannel = null;
try {
inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ);
outChannel = FileChannel.open(Paths.get("x.jpg"), StandardOpenOption.READ,StandardOpenOption.WRITE,StandardOpenOption.CREATE_NEW); MappedByteBuffer inMappedBuffer = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size());
MappedByteBuffer outMappedBuffer = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size()); System.out.println(inMappedBuffer.limit());
byte[] b = new byte[inMappedBuffer.limit()];;
inMappedBuffer.get(b);
outMappedBuffer.put(b); } catch (Exception e) {
e.printStackTrace();
} finally { if(inChannel != null){
try {
inChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
} if(outChannel != null){
try {
outChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
} }

 

2.3 重点 NIO-非阻塞IO

个人认为 NIO 最难的两点 一个是对于选择器和选择键的理解 其次是对于网络通信模型的理解

本章内容以防过长 只讲解 NIO 的使用方法 上述两点参看下回分解

 

2.3.1 阻塞IO示例

	//客户端
@Test
public void client() throws IOException{
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898)); FileChannel inChannel = FileChannel.open(Paths.get("1.jpg"), StandardOpenOption.READ); ByteBuffer buf = ByteBuffer.allocate(1024); while(inChannel.read(buf) != -1){
buf.flip();
sChannel.write(buf);
buf.clear();
} sChannel.shutdownOutput(); //接收服务端的反馈
int len = 0;
while((len = sChannel.read(buf)) != -1){
buf.flip();
System.out.println(new String(buf.array(), 0, len));
buf.clear();
} inChannel.close();
sChannel.close();
} //服务端
@Test
public void server() throws IOException{
ServerSocketChannel ssChannel = ServerSocketChannel.open(); FileChannel outChannel = FileChannel.open(Paths.get("2.jpg"), StandardOpenOption.WRITE, StandardOpenOption.CREATE); ssChannel.bind(new InetSocketAddress(9898)); SocketChannel sChannel = ssChannel.accept(); ByteBuffer buf = ByteBuffer.allocate(1024); while(sChannel.read(buf) != -1){
buf.flip();
outChannel.write(buf);
buf.clear();
} //发送反馈给客户端
buf.put("服务端接收数据成功".getBytes());
buf.flip();
sChannel.write(buf); sChannel.close();
outChannel.close();
ssChannel.close();
}

 

2.3.2 非阻塞IO示例-TCP

//客户端
@Test
public void client() throws IOException{
//1. 获取通道
SocketChannel sChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898)); //2. 切换非阻塞模式
sChannel.configureBlocking(false); //3. 分配指定大小的缓冲区
ByteBuffer buf = ByteBuffer.allocate(1024); //4. 发送数据给服务端
Scanner scan = new Scanner(System.in); while(scan.hasNext()){
String str = scan.next();
buf.put((new Date().toString() + "\n" + str).getBytes());
buf.flip();
sChannel.write(buf);
buf.clear();
} //5. 关闭通道
sChannel.close();
} //服务端
@Test
public void server() throws IOException{
//1. 获取通道
ServerSocketChannel ssChannel = ServerSocketChannel.open(); //2. 切换非阻塞模式
ssChannel.configureBlocking(false); //3. 绑定连接
ssChannel.bind(new InetSocketAddress(9898)); //4. 获取选择器
Selector selector = Selector.open(); //5. 将通道注册到选择器上, 并且指定“监听接收事件”
ssChannel.register(selector, SelectionKey.OP_ACCEPT); //6. 轮询式的获取选择器上已经“准备就绪”的事件
while(selector.select() > 0){ //7. 获取当前选择器中所有注册的“选择键(已就绪的监听事件)”
Iterator<SelectionKey> it = selector.selectedKeys().iterator(); while(it.hasNext()){
//8. 获取准备“就绪”的是事件
SelectionKey sk = it.next(); //9. 判断具体是什么事件准备就绪
if(sk.isAcceptable()){
//10. 若“接收就绪”,获取客户端连接
SocketChannel sChannel = ssChannel.accept(); //11. 切换非阻塞模式
sChannel.configureBlocking(false); //12. 将该通道注册到选择器上
sChannel.register(selector, SelectionKey.OP_READ);
}else if(sk.isReadable()){
//13. 获取当前选择器上“读就绪”状态的通道
SocketChannel sChannel = (SocketChannel) sk.channel(); //14. 读取数据
ByteBuffer buf = ByteBuffer.allocate(1024); int len = 0;
while((len = sChannel.read(buf)) > 0 ){
buf.flip();
System.out.println(new String(buf.array(), 0, len));
buf.clear();
}
} //15. 取消选择键 SelectionKey
it.remove();
}
}
}

 

2.3.3 非阻塞IO示例-UDP

	@Test
public void send() throws IOException{
DatagramChannel dc = DatagramChannel.open(); dc.configureBlocking(false); ByteBuffer buf = ByteBuffer.allocate(1024); Scanner scan = new Scanner(System.in); while(scan.hasNext()){
String str = scan.next();
buf.put((new Date().toString() + ":\n" + str).getBytes());
buf.flip();
dc.send(buf, new InetSocketAddress("127.0.0.1", 9898));
buf.clear();
} dc.close();
} @Test
public void receive() throws IOException{
DatagramChannel dc = DatagramChannel.open(); dc.configureBlocking(false); dc.bind(new InetSocketAddress(9898)); Selector selector = Selector.open(); dc.register(selector, SelectionKey.OP_READ); while(selector.select() > 0){
Iterator<SelectionKey> it = selector.selectedKeys().iterator(); while(it.hasNext()){
SelectionKey sk = it.next(); if(sk.isReadable()){
ByteBuffer buf = ByteBuffer.allocate(1024); dc.receive(buf);
buf.flip();
System.out.println(new String(buf.array(), 0, buf.limit()));
buf.clear();
}
} it.remove();
}
}

java核心技术-NIO的更多相关文章

  1. java核心技术-(总结自杨晓峰-java核心技术36讲)

    1. 谈谈你对java平台的理解 首先是java最显著的两个特性,一次写入处处运行:还有垃圾收集器gc,gc能够对java内存进行管理回收,程序员不需要关心内存的分配和回收问题 然后谈谈jre和jdk ...

  2. 杨晓峰-Java核心技术-6 动态代理 反射 MD

    目录 第6讲 | 动态代理是基于什么原理? 典型回答 考点分析 知识扩展 反射机制及其演进 动态代理 精选留言 Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAnd ...

  3. java核心技术卷一

    java核心技术卷一 java基础类型 整型 数据类型 字节数 取值范围 int 4 +_2^4*8-1 short 2 +_2^2*8-1 long 8 +_2^8*8-1 byte 1 -128- ...

  4. Java核心技术中的程序片段

    import java.io.*; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import ...

  5. Java核心技术

    [Java核心技术36讲]1.谈谈你对Java平台的理解 2.Exception和Error有什么区别 3.谈谈final.finally.finalize有什么不同?4.强引用.软引用.弱引用.虚引 ...

  6. 2019 最新 Java 核心技术教程,都在这了!

    Java技术栈 www.javastack.cn 优秀的Java技术公众号 以下是Java技术栈微信公众号发布的所有关于 Java 的技术干货,会从以下几个方面汇总,本文会长期更新. Java 基础篇 ...

  7. 面试必备!Java核心技术100+面试题

    一线互联网公司工作了几年,我作为求职者参加了不少面试,也作为面试官面试了很多同学,整理这份面试指南,一方面是帮助大家更好的准备面试,有的放矢,另一方面也是对自己知识框架做一个体系化的梳理. 这篇文章梳 ...

  8. JAVA bio nio aio

    [转自]http://qindongliang.iteye.com/blog/2018539 在高性能的IO体系设计中,有几个名词概念常常会使我们感到迷惑不解.具体如下: 序号 问题 1 什么是同步? ...

  9. Java核心技术点之泛型

    1. Why ——引入泛型机制的原因 假如我们想要实现一个String数组,并且要求它可以动态改变大小,这时我们都会想到用ArrayList来聚合String对象.然而,过了一阵,我们想要实现一个大小 ...

随机推荐

  1. [ActionScript3.0] 使用FileReferenceList处理多个文件上载

    package { import flash.display.Sprite; import flash.events.DataEvent; import flash.events.Event; imp ...

  2. 【bug】—— H5页面在 ios 端滑动不流畅的问题

    IOS系统的惯性滑动效果非常6,但是当我们对div加overflow-y:auto;后是不会出这个效果的,滑动的时候会感觉很生涩.怎么办? 方案一: 在滚动容器内加-webkit-overflow-s ...

  3. [转] FFmpeg常用基本命令

    [FFmpeg]FFmpeg常用基本命令 1.分离视频音频流 ffmpeg -i input_file -vcodec copy -an output_file_video //分离视频流 ffmpe ...

  4. 通过API方式查看Azure Sign-ins记录

    经确认,目前Sign-ins功能在中国区还没有开通.也没有相关的预计开通时间.您可以通过如下链接随时关注China Azure的最新公告:https://www.azure.cn/zh-cn/what ...

  5. OKR 第一阶段

    性能优化,主要是为了提高用户体验. 1.  根据浏览器的工作原理,dom树解析时,遇到css 以及js 会出现阻塞,为了缩短dom树解析时间,进行了js  增加 async 的异步加载过程 . 原有代 ...

  6. 新人须知的网站文件和MySQL数据库备份流程思路

    昨天老左再次遇到一个网友告知使用的一台服务器自己无意中点击主机商面板的导致服务器被重新安装系统(居然这也可以),问问是否可以恢复数据.这个同学和之前遇到好几次的网友真相似,从开始购买服务器,到自己网站 ...

  7. linux系统服务管理

    centos7的服务管理命令 systemctl start 服务名称 systemctl stop 服务名称 systemctl status 服务名称 systemctl restart 服务名称 ...

  8. 获得自己电脑的SSH公匙

    关于什么是SSH请点击此"www.Baidu.com”网站了解 我这里只说怎么获取属于自己电脑的SSH公匙 本人是Win10电脑 所以相对来说简单一点  点击win ->选择设置-&g ...

  9. Windows开发经验 - Visual Studio 2017

    1. 调试子进程 Visual Studio 2017及更早的版本原生不支持调试子进程,不确定未来是否会支持.可以通过官方插件让Visual Studio能够调试子进程. https://market ...

  10. win10 压缩包安装mysql8.0.11报错:Access denied for user 'root'@'localhost'

    按这篇:https://blog.csdn.net/Myuhua/article/details/84792121#commentsedit 这里精简下,还有update语句中authenticati ...