浅析Java NIO
浅析Java NIO
前言
在说NIO之前,先来说说IO的读写原理。我们都知道Java中的IO流可以分为网络IO流和文件IO流,前者在网络中使用,后者在操作文件时使用。但实际上两种流区别并不是太大,对于操作系统来说区别仅仅是和硬盘打交道还是和网卡打交道。
其次,我们直接操控的是Jvm虚拟机,虚拟机是运行在操作系统上的、用户层面的进程,jvm虚拟机并不能直接操控底层硬件(这也是为什么Java很少用来做坏事的原因之一),而是向系统进行发出调用申请。
因此,当Jvm运行到IO流的read方法后会向系统发出read系统调用,由系统使用硬盘驱动来从硬盘读取数据(这只是个简单的比喻,实际情况是有点复杂的)。需要注意的是系统并不会直接把数据从硬盘复制到Jvm内存中,而是把数据先复制到一个“内核缓冲区”的地方。我们使用字节流时都会new一个byte数组作为缓冲区,这个缓冲区是用户缓冲区,内核中也存在这样一个缓冲区。所以一个常规的IO流读取文件的过程是这样的:硬盘 -> 内核缓冲区 -> 用户缓冲区(Jvm内存,也就是byte数组)
,写操作也是同样的道理。
当内核没有准备好数据的时候,整个用户进程是阻塞的,直到系统吧数据从内核缓存移动到jvm内存中后整个进程才会继续运行下去。系统从本地文件读取数据时可能会快一点,但是当从网卡读取数据时由于网络延迟的存在,其效率会非常低,并且一个线程只能处理一个网络请求。
如果有多个客户端访问时虽然可以开多线程来处理,但是线程是一种“非常贵”的资源,无论线程是否工作,虚拟机会为每个线程至少保留128K~1M的空间,并且当线程多了之后,线程之间争抢资源、CPU频繁切换不同线程会导致整个系统都效率低下(切换线程需要保存当前线程上下文,浪费CPU性能)。
何为NIO & 为什么使用NIO
什么是NIO:
NIO的官方解释是:NIO stands for non-blocking I/O(并非某些人所说的 new IO),直译就是非阻塞IO。需要说明的是Java中的NIO并不属于非阻塞IO模型,而是IO复用模型,不过同样实现了非阻塞状态。
与普通IO的不同
普通的IO的核心是Stream
,NIO的核心是Buffer
( 缓存区)、Channel
(通道)和Selector
(选择器)。
为什么使用NIO
需要明白的是NIO解决了网络中一个线程只能处理单个连接的痛点。还可以减少文件传输时CPU在存储中反复拷贝的副作用,即减少了系统内核和用户应用程序地址空间这两者之间进行数据拷贝,这就是零拷贝(zero copy)技术)。
那么什么是Buffer
(缓存区)、Channel
(通道)和Selector
(选择器)呢?
Buffer这个比较好理解,就是一个用来存放数据的地方,即缓冲区。Channel则有点像流,不过Channel是双向的,数据可以从Buffer读取到channel中,也可以从channel中写入到buffer。
Selector则是选择器,用来对多个channel进行筛选,进而挑出可以处理的channel,这样就把多线程级别的处理降级为单线程的轮询,不用我们手动维护线程池而交给selector来处理。需要注意的是调用selector的select()方法后如果没有可用的channel,此时该线程是阻塞的。
Buffer
Buffer(缓冲区)和使用普通IO流时创建的byte数组区别并不大,只不过封装成了类并添加了一些特有的方法。
Buffer的翻译是什么?缓冲啊,缓冲区是干什么的?存取数据呗,怎么存?put、get呀。因此Buffer的核心方法便是put()
和get()
。
同时,Buffer维护了四个变量来描述这个数据缓冲区的状态:
- 容量Capacity:缓冲区能够容纳的数据元素的最大数量。容量在缓冲区创建时被设定,并且永远不能被改变。(不能被改变的原因也很简单,底层是数组嘛)
- 上界Limit:缓冲区里的数据的总数(能够存或者读的界限),代表了当前缓冲区中一共有多少数据。
- 位置Position:下一个要被读或写的元素的位置。Position会自动由相应的 get( )和 put( )函数更新。
直接用起来大概就是这个样子:
//使用allocate()方法构建缓冲区,分配大小为128字节
ByteBuffer byteBuffer = ByteBuffer.allocate(128);
//写入数据
byteBuffer.put("Java".getBytes());
//切换模式
byteBuffer.flip();
while (byteBuffer.hasRemaining()){//Remaining : 剩余
System.out.println((char)byteBuffer.get());
}
看flip()
的源码就会发现也就这样(flip : 翻动):
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
想重新写入数据可以调用clear()
方法:
public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
没错,clear后数据还在,只不过position归0不能读了。想重新读取可以调用rewind()
方法(rewind : 倒带):
public final Buffer rewind() {
position = 0;
mark = -1;
return this;
}
那如果读取到一半又想写入了怎么办呢?可以调用compact()
方法,这个方法可以将所有未读的数据拷贝到Buffer起始处。然后将position设到最后一个未读元素正后面。limit属性依然像clear()方法一样,设置成capacity。现在Buffer准备好写数据了,但是不会覆盖未读的数据。(compact : 紧凑)
调用position()
方法可以获得当前position的位置。
可能有同学发现了,上面我说这个类维护了四个变量来描述缓冲区,我却只列出了三个,并且在源代码中频繁出现了mark
这个关键字,没错,这就是第四个变量,用来当做作为一个标记。可以调用mark()
方法来标记此时的position的位置,然后调用reset()
方法将position回到此处,下面是源码:
public final Buffer mark() {
mark = position;
return this;
}
public final Buffer reset() {
int m = mark;
if (m < 0)
throw new InvalidMarkException();
position = m;
return this;
}
简单粗暴
浅析Java NIO的更多相关文章
- Java NIO浅析 转至 美团技术团队
出处: Java NIO浅析 NIO(Non-blocking I/O,在Java领域,也称为New I/O),是一种同步非阻塞的I/O模型,也是I/O多路复用的基础,已经被越来越多地应用到大型应用服 ...
- Java NIO:浅析I/O模型
也许很多朋友在学习NIO的时候都会感觉有点吃力,对里面的很多概念都感觉不是那么明朗.在进入Java NIO编程之前,我们今天先来讨论一些比较基础的知识:I/O模型.下面本文先从同步和异步的概念 说起, ...
- Java NIO:浅析I/O模型(转)
原文链接:http://www.cnblogs.com/dolphin0520/p/3916526.html 以下是本文的目录大纲: 一.什么是同步?什么是异步? 二.什么是阻塞?什么是非阻塞? 三. ...
- java NIO中的Reactor相关知识汇总 (转)
一.引子 nio是java的IO框架里边十分重要的一部分内容,其最核心的就是提供了非阻塞IO的处理方式,最典型的应用场景就是处理网络连接.很多同学提起nio都能说起一二,但是细究其背后的原理.思想往往 ...
- 浅析java内存管理机制
内存管理是计算机编程中的一个重要问题,一般来说,内存管理主要包括内存分配和内存回收两个部分.不同的编程语言有不同的内存管理机制,本文在对比C++和Java语言内存管理机制的不同的基础上,浅析java中 ...
- Java NIO系列教程(一) Java NIO 概述
<I/O模型之四:Java 浅析I/O模型> 一.阻塞IO与非阻塞IO 阻塞IO: 通常在进行同步I/O操作时,如果读取数据,代码会阻塞直至有 可供读取的数据.同样,写入调用将会阻塞直至数 ...
- Java nio 空轮询bug到底是什么
编者注:Java nio 空轮询bug也就是Java nio在Linux系统下的epoll空轮询问题. epoll机制是Linux下一种高效的IO复用方式,相较于select和poll机制来说.其高效 ...
- 源码分析netty服务器创建过程vs java nio服务器创建
1.Java NIO服务端创建 首先,我们通过一个时序图来看下如何创建一个NIO服务端并启动监听,接收多个客户端的连接,进行消息的异步读写. 示例代码(参考文献[2]): import java.io ...
- 支撑Java NIO 与 NodeJS的底层技术
支撑Java NIO 与 NodeJS的底层技术 众所周知在近几个版本的Java中增加了一些对Java NIO.NIO2的支持,与此同时NodeJS技术栈中最为人称道的优势之一就是其高性能IO,那么我 ...
随机推荐
- checkbox 选中获取值
1:jsp 页面 从页面获取checkbox 的值传到后台 <div id="divCheckbox" class="hide"> <tr&g ...
- 2020牛客寒假算法基础集训营4 J 二维跑步
https://ac.nowcoder.com/acm/contest/view-submission?submissionId=43035417 假设有i步选择不动,就有n-i步移动 假设其中又有a ...
- js 表格操作----添加删除
js 表格操作----添加删除 书名:<input type="text" id="name"> 价格:<input type="t ...
- Go 开发者平均年薪 46 万?爬数据展示国内 Go 的市场行情到底如何
随着云原生时代的到来,拥有高并发性.语法易学等特点的 Golang 地位逐渐凸显,在云原生编程中占据了主导地位.在近期出炉的 TIOBE 10 月编程语言排行榜中,Golang 从前一个月的 16 位 ...
- LeetCode刷题--21.合并两个有序链表(简单)
题目描述 将两个有序链表合并为一个新的有序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 示例: 输入:1 -> 2 -> 4 ,1 -> 3 -> 4 输出:1 ...
- C语言动静态链接库使用(笔记)
看了视频一直没空写........... C静态链接库不用说了跟你写在cpp文件里的函数一样不会有单独的模块 不再赘述生活中用的比较少 例子 .h文件 int Plus(int x, int y); ...
- GitHub fork 合作开发 - 快速实现版
目录 一 预备条件 二 fork项目 三 将项目clone到本地 四 push代码到自己的仓库 五 通过pull request提交代码 六 通过本地配置upstream来同步更新主repo的内容 七 ...
- 7.2 Varnish 模式
- 每个项目中,你必须知道的11个Java第三方类库。
Java第三方library ecosystem是一个很广阔的范畴.不久前有人撰文:每个项目中,你必须知道的11个Java第三方类库. 单元测试 1.DBUnit DBunit是一个基于junit扩展 ...
- 神奇的URL Schemes大全
微信: 打开微信 wechat:// 微信扫一扫 weixin://scanqrcode 支付宝: 蚂蚁庄园 alipays://platformapi/startapp?appId=66666674 ...