浅析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的更多相关文章

  1. Java NIO浅析 转至 美团技术团队

    出处: Java NIO浅析 NIO(Non-blocking I/O,在Java领域,也称为New I/O),是一种同步非阻塞的I/O模型,也是I/O多路复用的基础,已经被越来越多地应用到大型应用服 ...

  2. Java NIO:浅析I/O模型

    也许很多朋友在学习NIO的时候都会感觉有点吃力,对里面的很多概念都感觉不是那么明朗.在进入Java NIO编程之前,我们今天先来讨论一些比较基础的知识:I/O模型.下面本文先从同步和异步的概念 说起, ...

  3. Java NIO:浅析I/O模型(转)

    原文链接:http://www.cnblogs.com/dolphin0520/p/3916526.html 以下是本文的目录大纲: 一.什么是同步?什么是异步? 二.什么是阻塞?什么是非阻塞? 三. ...

  4. java NIO中的Reactor相关知识汇总 (转)

    一.引子 nio是java的IO框架里边十分重要的一部分内容,其最核心的就是提供了非阻塞IO的处理方式,最典型的应用场景就是处理网络连接.很多同学提起nio都能说起一二,但是细究其背后的原理.思想往往 ...

  5. 浅析java内存管理机制

    内存管理是计算机编程中的一个重要问题,一般来说,内存管理主要包括内存分配和内存回收两个部分.不同的编程语言有不同的内存管理机制,本文在对比C++和Java语言内存管理机制的不同的基础上,浅析java中 ...

  6. Java NIO系列教程(一) Java NIO 概述

    <I/O模型之四:Java 浅析I/O模型> 一.阻塞IO与非阻塞IO 阻塞IO: 通常在进行同步I/O操作时,如果读取数据,代码会阻塞直至有 可供读取的数据.同样,写入调用将会阻塞直至数 ...

  7. Java nio 空轮询bug到底是什么

    编者注:Java nio 空轮询bug也就是Java nio在Linux系统下的epoll空轮询问题. epoll机制是Linux下一种高效的IO复用方式,相较于select和poll机制来说.其高效 ...

  8. 源码分析netty服务器创建过程vs java nio服务器创建

    1.Java NIO服务端创建 首先,我们通过一个时序图来看下如何创建一个NIO服务端并启动监听,接收多个客户端的连接,进行消息的异步读写. 示例代码(参考文献[2]): import java.io ...

  9. 支撑Java NIO 与 NodeJS的底层技术

    支撑Java NIO 与 NodeJS的底层技术 众所周知在近几个版本的Java中增加了一些对Java NIO.NIO2的支持,与此同时NodeJS技术栈中最为人称道的优势之一就是其高性能IO,那么我 ...

随机推荐

  1. Android编程实现点击链接打开APP功能示例

    本文实例讲述了Android编程实现点击链接打开APP功能.分享给大家供大家参考,具体如下: 在Android中点击链接打开APP是一个很常见的需求.例如,电商为用户发送优惠券之后经常会下发一条短信: ...

  2. git 常用命令记录 -- 快捷&备忘

    1.安装 略2.git拉取远程分支 git config user.name git config user.email git config --global user.name xxxx git ...

  3. Firefox 的 hack 及 问题

    hack: @-moz-document url-prefix(){ #selector{ display: flex; justify-content: space-between; } } 问题一 ...

  4. 常见Http状态码大全详解

    HTTP状态码的分类 HTTP状态码由三个十进制数字组成,第一个十进制数字定义了状态码的类型,后两个数字没有分类的作用.HTTP状态码共分为5种类型: 具体如下: 状态码 内容 详细内容 信息告知 - ...

  5. python爬虫处理在线预览的pdf文档

    引言 最近在爬一个网站,然后爬到详情页的时候发现,目标内容是用pdf在线预览的 比如如下网站: https://camelot-py.readthedocs.io/en/master/_static/ ...

  6. primecoin 全节点日常维护操作

    primecoin 全节点日常维护操作: 一.关于primecoin维护,每天检查这6个地址是否能正常访问: http://api.primecoin.org/rest/pcoin/syncblock ...

  7. new Vue() 和 export default {}及Vue页面组件和标签组件说明与比较(非常重要)

    说明与比较:new Vue() 和 export default {} (1)vue就是一个构造函数 (2)vue标签组件:是HTML标签的扩展https://www.cnblogs.com/w-wa ...

  8. MariaDB——数据库基础与sql语句

    数据库介绍 什么是数据库? 简单的说,数据库就是一个存放数据的仓库,这个仓库是按照一定的数据结构(数据结构是指数据的组织形式或数据之间的联系)来组织,存储的,我们可以通过数据库提供的多种方法来管理数据 ...

  9. Waiting for table metadata lock 错误

    在 alter table 时,必然出现了 lock 错误,检查 processlist ,全都是 sleep 状态,然后检查 information_schema.innodb_trx 发现一个活跃 ...

  10. Java服务端对Cookie的简单操作

    Java服务端对Cookie的简单操作 时间 2016-04-07 10:39:44 极客头条 原文  http://www.cuiyongzhi.com/index.php/post/15.html ...