浅析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. Hive的存储和MapReduce处理——数据清洗(Part3)

    日期:2019.11.17 博客期:118 星期日 这几天在写程序的时候虚拟机崩了,无语~所以重新从最初的状态开始配环境,重新整理之前的所有代码程序.

  2. Kubernetes 深入学习(一) —— 入门和集群安装部署

    一.简介 1.Kubernetes 是什么 Kubernetes 是一个全新的基于容器技术的分布式架构解决方案,是 Google 开源的一个容器集群管理系统,Kubernetes 简称 K8S. Ku ...

  3. 如何更改linux(centos)下的Apache http端口号

    # vi  /etc/httpd/conf/httpd.conf  文件 修改两个地方     #Listen 12.34.56.78:80     Listen 80     #把80改为你设置的端 ...

  4. 三星 S10 运行 Ubuntu 系统

    导读 DeX 是一种模仿桌面操作系统的用户 UI 界面,把支持 DeX 的三星手机用数据线连上外置显示器,用户就可以获得一种类似桌面系统的使用体验. 三星 S8.Note 8.S9.Note 9.S1 ...

  5. webpack配置自动打包重新运行npm run dev出现报错

    webpack配置自动打包重新运行npm run dev出现报错 运行npm run dev出现如下报错 Listening at http://localhost:8080(node:2328) U ...

  6. 树莓派frp添加为服务管理

    1.下载frp https://github.com/fatedier/frp/releases 我是1代的B+,下载arm版的,新的可以用arm64的 frp_0.29.0_linux_arm.ta ...

  7. 搞懂G1垃圾收集器

    一.G1 GC术语Overview 1.1 并发 并发的意思是Java应用执行和垃圾收集活动可以同时进行 1.2 并行 并行的意思是垃圾收集运算是多线程执行的,比如CMS垃圾收集器的年轻代就是并行的, ...

  8. poj 3069 Saruman's Army 贪心模拟

    Saruman's Army Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 18794   Accepted: 9222 D ...

  9. NO30 磁盘分区--Raid--ext2文件系统

    Raid: ext2文件系统:

  10. 数据库建表并返回给前端 - (mysql-thinkphp) (3)

    1.先建一个表,你可以用mysql代码建,也可以用thinkphp建,也可以视图建,我用不到太复杂的,就用视图建了. 2.配置id为自增,唯一的值,不可重复.主键.要输入中文的选择utf8_gener ...