Deep Dive 3 - NIO

我们来继续80后Deep Dive 3 - NIO。

Java NIO(New IO)是Java 1.4中引入的,时间的话已经是2002年了,确实久远。NIO的全称是New IO,作者偷懒就直接称之为NIO, 反而听起来酷酷的。

1. NIO 之父

老样子,我们先看看NIO的作者,NIO之父Mark Reinhold。

Mark大叔毕业于MIT Ph.D.,老SUN员工了。大叔1996年加入Sun,一待就是13年,全心开发Java,后来Oracle于2010年收购SUN,被迫变成Oracle员工,担任Java首席架构师一直到现在,说白了一直从事Java开发19年了,不对是“一直开发Java19年”, 区别大了。

大牛有一段自我介绍中提及了Sun,很有意思, 只可意会。

Prior to working at Oracle I did much the same thing at Sun
Microsystems
, a great company which was too lucky for its own good during the boom years and subsequently driven
into the ground by a false prophet who was long on vision and short on execution.

http://mreinhold.org/

不小心故意瞅了一眼大师的朋友圈,好家伙,果然大师的朋友圈都是大神。

看到第一个了么?Java之父啊。没个XXO头衔都不好意思加好友。

2. NIO 概述

可以看出NIO核心主要提供了Channel与Buffer,以及隐含在包中的异步IO与Selector。

  • Channel与Buffer : 标准的IO是基于字节流与字符流操作的, 而NIO是基于Channel通道与Buffer缓冲区的。
  • Non-Blocking IO  : NIO可以做到非阻塞IO(除文件IO),如线程可以Channel读取数据到Buffer, 同时还可以并行做其他事情;当数据写入缓冲区后可以继续;
  • Selector :选择器Selector可以用来监听多个事件,如单个线程监听多个数据通道等。

2.1 Channel

Channel接口定义很清爽,少到几乎不看注视不知道干嘛。好吧,注释有云:Channel代表了与一个实体对象entity的连接,如文件,网络socket, 甚至硬件设备等。

大体来说,Channel的目的是为其子类及实现支持并发多线程访问的安全环境。Channel有点类似我们熟悉的流,数据可以与Buffer进行双向交互。

如图:(图来自jenkov.com/)

主要实现有以下类涵盖了文件,TCP, UDP.

  • FileChannel
  • DatagramChannel
  • SocketChannel
  • ServerSocketChannel

2.2 Buffer

Buffer简单来说是一块可被读写的内存数据块,封装成Buffer对象用来交互。

Buffer根据数据类型又分成如上几种。Buffer有几个核心属性我们需要了解一下。

上图:

首先,Buffer区分读模式与写模式。

  • Capacity 即其Buffer的总容量;单位为当前数据类型,如1024 capacity long数据。可以在其满了以后,清空(读数据/清除)。
  • Position 请看图即可;No? 写模式就是当前写的位置,初始为0, 并随着数据的写入而动态变化;通过flip切换到读模式时,首先重置0,当读取数据时,position会动态移动到下一个可读位置。
  • Limit    写模式下表示最多能写多少数据,等于capacity;切换到读模式后,limit会设置成为写模式的position位置,即可以读到之前写入的所有数据;
  • flip()    将Buffer从写模式切换到读模式,即会将position设为0, 并将limit设置为之前的position值。

Buffer 可以通过allocate方法来分配, 如:

  1. CharBuffer buf = CharBuffer.allocate(1024);

看一个简单例子:

稍微提及一下java的基本IO操作模型,以文件copy为例:

  • 开启输入流将文件读入内存:输入数据先进入Kernel区域,再copy到JVM
  • 开启输出流将内存中的数据输出到另一个文件: 先由JVM cop到Kernel再到终端

ByteBuffer

其中ByteBuffer又分为HeapByteBuffer与DirectByteBuffer.

显然,一个是在java堆中分配空间,一个是在c heap中分配空间,即非gc托管,无法进行垃圾回收。所以,c heap的buffer需要手工释放。

所以,按照我们上文提及的基本IO操作模型,direct buffer显然省掉了jvm copy这个过程,速度会加快。

另外,稍微提及一下,Off-Heap Buffer经常用来做大对象缓存, 为什么? 因为这样可以脱离GC管理,不被垃圾回收掉。

2.2 Non-Blocking iO

Blocking
v.s. Non-Blocking:  阻塞IO是在调用某方法时线程是处于阻塞的,它会一直等待数据返回或者超时才返回,如InputStream.read();
 ServerSocket.accept(); 说的再细点的话,结合操作模型,程序首先发送请求给内核kernel,然后内核去进行网路通信,如果是read(), 在内核准备好数据前,这个线程一直会被挂起,一直等待。

详细过程如下:

有了上面的铺垫,则对应的非阻塞则好理解了:

一个明显区别就是,当发起第一次call请求后,Non-Blocking的线程并没有被阻塞,但它也没有去做别的事,而是不断的发起call请求去检测等待返回。这样的机制,其实只用一个线程来监听,其它线程都可以去做其他事情, 从而引出了NIO中的selector机制。

Selector选择机制:

Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件。这样,一个单独的线程可以管理多个channel,从而管理多个网络连接。

双向通道channel上我们可以注册我们感兴趣的事件:

事件名 对应值
服务端接收客户端连接事件 SelectionKey.OP_ACCEPT(16)
客户端连接服务端事件 SelectionKey.OP_CONNECT(8)
读事件 SelectionKey.OP_READ(1)
写事件 SelectionKey.OP_WRITE(4)

代码如下:

值得注意的是,NIO中只支持SocketChannel与ServerSocketChannel,
FileChannel是不能实现NIO的。但我们知道上面的io与内核的交互以及DirectByteBuffer,仍然有办法加速文件的io。

2.4 AIO (Asynchronous I/O) = NIO 2.0

Java 1.7又引入了AIO, 即异步IO。细心的读者观察到,上面Non-Blocking的线程并没有被阻塞,但它也没有去做别的事,而是不断的发起call请求去检测等待返回。而可以更佳智能就好了。

想到以前看过的一本书的例子,以物流快递为例,名字忘记了。

  • BlockingIO
    : 你要去物流快递公司等货,并且不能离开,一直等下去。
  • Non-Blocking:你只要每天去物流快递公司看一下,甚至一个小区可以派一个人去看看整个小区有没有快递,如selector模式。
  • Asynchronous I/O:快递来了, 您只要签收就好。

好吧, 原来快递行业玩的也是高大上的AIO, 哈哈。

好吧,时间不早了,要上班去了。写太多看起来也不方便。抛砖引玉,希望对大家有帮助。

公众号:技术极客TechBooster

本文有些图片解释参考自:

http://tutorials.jenkov.com/java-nio/overview.html

时间: 2024-10-25 05:25:13

Deep Dive 3 - NIO的相关文章

A Deep Dive into Recurrent Neural Nets

A Deep Dive into Recurrent Neural Nets Last time, we talked about the traditional feed-forward neural net and concepts that form the basis of deep learning. These ideas are extremely powerful! We saw how feed-forward convolutional neural networks hav

X64 Deep Dive

zhuan http://www.codemachine.com/article_x64deepdive.html X64 Deep Dive This tutorial discusses some of the key aspects of code execution on the X64 CPU like compiler optimizations, exception handling, parameter passing and parameter retrieval and sh

VXLAN Deep Dive

http://www.definethecloud.net/vxlan-deep-dive/ I've been spending my free time digging into network virtualization and network overlays.  This is part 1 of a 2 part series, part 2 can be found here: http://www.definethecloud.net/vxlan-deep-divepart-2

《Docker Deep Dive》Note - Docker 引擎

<Docker Deep Dive>Note Docker 引擎 1. 概览 graph TB A(Docker client) --- B(daemon) subgraph Docker 引擎 B --- C(containerd) C --- D(runc) end Docker 引擎是用来运行和管理容器的核心软件. 主要构成:Docker Client.Docker daemon(Docker守护进程).containerd.runc. 2. 详解 graph TB A(Docker c

《Docker Deep Dive》Note - 纵观 Docker

<Docker Deep Dive>Note 由于GFW的隔离,国内拉取镜像会报TLS handshake timeout的错误:需要配置 registry-mirrors 为国内源解决这个问题. 可以配置为阿里的加速源:https://cr.console.aliyun.com/undefined/instances/mirrors,阿里的加速器可以提升获取Docker官方镜像的速度. 登录开发者账号后,将自己的加速器地址复制到 Docker Settings > Daemon >

SQL optimizer -Query Optimizer Deep Dive

refer: http://sqlblog.com/blogs/paul_white/archive/2012/04/28/query-optimizer-deep-dive-part-1.aspx    SQL是一种结构化查询语言规范,它从逻辑是哪个描述了用户需要的结果,而SQL服务器将这个逻辑需求描述转成能执行的物理执行计划,从而把结果返回给用户.将逻辑需求转换成一个更有效的物理执行计划的过程,就是优化的过程. 执行SQL的过程: Input Tree We start by looking

DM9000驱动移植在mini2440(linux2.6.29)和FS4412(linux3.14.78)上的实现(deep dive)篇一

关于dm9000的驱动移植分为两篇,第一篇在mini2440上实现,基于linux2.6.29,也成功在在6410上移植了一遍,和2440非常类似,第二篇在fs4412(Cortex A9)上实现,基于linux3.14.78,用设备树匹配,移植过程中调试和整体理解很重要,一路上幸有良师益友指点,下面详细介绍: 1.物理时序分析相关 DM9000芯片是DAVICOM公司生产的一款以太网处理芯片,提供一个通用的处理器接口.一个10/100M自适应的PHY芯片和4K双字的SRAM.内部框架如下,涉及

SPI在linux3.14.78 FS_S5PC100(Cortex A8)和S3C2440上驱动移植(deep dive)

由于工作的原因,对SPI的理解最为深刻,也和SPI最有感情了,之前工作都是基于OSEK操作系统上进行实现,也在US/OS3上实现过SPI驱动的实现和测试,但是都是基于基本的寄存器操作,没有一个系统软件架构的思想,感觉linux SPI驱动很强大,水很深,废话少说,SPI总线上有两类设备:一类是主机端,通常作为SOC系统的一个子模块出现,比如很多嵌入式MPU中都常常包含SPI模块.一类是从机被控端,例如一些SPI接口的Flash.传感器等等.主机端是SPI总线的控制者,通过使用SPI协议主动发起S

A Deep Dive into PL/v8

Back in August, Compose.io announced the addition of JavaScript as an internal language for all new PostgreSQL deployments. This was thanks to the PL/v8 project, which straps Google's rocket of a JavaScript engine (V8) to PostgreSQL. This got me thin