浅析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,那么我 ...
随机推荐
- MAC97A6检测
- 第4节 Scala中的actor介绍:1、actor概念介绍;2、actor执行顺序和发送消息的方式
10. Scala Actor并发编程 10.1. 课程目标 10.1.1. 目标一:熟悉Scala Actor并发编程 10.1.2. 目标二:为学习Akka做准备 注:Sca ...
- nginx sendfile 相关知识
https://blog.csdn.net/wm_1991/article/details/51916027
- ORACLE 判断首字母大小写问题
1.对判断的字段进行拆分 select substr(要区分的字段,0,1) from 表 : 得到一个 首字母 2.对这个字符进行大小写判断 查出以小写字符为开头的 select substr ...
- LeetCode 101.对称二叉树 - JavaScript
题目描述:给定一个二叉树,检查它是否是镜像对称的. 题目分析 下面这种二叉树就是镜像对称的,符合题目要求: 1 / \ 2 2 / \ / \ 3 4 4 3 解法 1:递归检查 根据题目" ...
- 常见Http状态码大全详解
HTTP状态码的分类 HTTP状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型,后两个数字没有分类的作用.HTTP状态码共分为5种类型: 具体如下: 状态码 内容 详细内容 信息告知 - ...
- python之对象基础
目录 面向对象 1. 面向过程编程的优缺点 2. 面向对象编程的优缺点 3. 类 类和函数的区别 什么是类 现实世界中先有对象,后有类 python中先有类,再有对象 对象 如何实例化一个对象 对象属 ...
- keep-alive 用法 及activated,deactivated这两个生命周期函数
keep-aliveProps: include - 字符串或正则表达式.只有名称匹配的组件会被缓存.exclude - 字符串或正则表达式.任何名称匹配的组件都不会被缓存.max - 数字.最多可以 ...
- 设计模式---JDK动态代理和CGLIB代理
Cglig代理设计模式 /*测试类*/ package cglibProxy; import org.junit.Test; public class TestCglib { @Test public ...
- 【剑指Offer面试编程题】题目1522:包含min函数的栈--九度OJ
题目描述: 定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数. 输入: 输入可能包含多个测试样例,输入以EOF结束. 对于每个测试案例,输入的第一行为一个整数n(1<=n&l ...