前言
最近在看java NIO的一些中文资料,确实比较的头疼。文章的好坏良莠不齐,好的文章一看,受益不少;讲的不好的,被引得偏差了3百里才绕回来。更有的只有NIO 1.0的内容,2.0的只字未提。本文主要目的是对NIO知识系统进行总结和梳理,并给大家提供一篇“能看”且较全面的文章。
一、操作系统I/O模型
学习NIO的前提是了解操作系统的I/O模型。
操作系统I/O模型
很明显,操作系统中存在着两个空间:内核空间和用户空间。用户空间就是常规的应用进程所在的空间,一个或者多个jvm实例当然也是一个用户态的进程,所以运行需要资源都在用户空间。内核空间是操作系统内核运行所在的空间。用户空间的进行没有权限直接跨过内核空间,直接跟硬件进行I/O交互。
当应用进程请求 I/O 操作的时候,JVM底层会执行一个系统调用(也就是说JVM所完成的任何操作,最终是通过native来实现),系统调用时,会将控制权移交给内核。操作系统的底层函数 open( )、read( )、write( )和 close( )来完成最终和硬件的交互。当内核完成调用后,得到了应用进程所需数据,它会在内核空间的缓存区保留数据,并把数据传送到用户空间内的指定缓冲区。内核试图对数据进行高速缓存或预读取,因此进程所需数据可能已经在内核空间里了。如果是这样,该数据只需简单地拷贝出来即可。如果数据不在内核空间,则进程被挂起,内核着手把数据读进内存。
现代操作系统常会用到DMA(Direct Memory Access,直接内存存取)技术,MMU(Memory Management Unit,内存管理单元)。关于这些内容,大家可以自行找谷哥。一些基于I/O中的技术经常也会提到一个词“zero-copy“,相关介绍可以看我的前一篇文章 http://go-on.iteye.com/blog/1807749。
虚拟内存
虚拟内存意为物理内存(硬件RAM)大小不够用的情况,通过操作系统的磁盘等设备,通过内存交换的方式来扩充内存。虚拟内存技术的利用,使得虚拟出来的内存能够远远大于实际的物理内存。因为内存地址也是虚拟的,所以就会存在两个不同的虚拟内存地址指向同一个物理内存。
磁盘控制器不能通过DMA对用户控件的内存进行访问。但是通过虚拟内存技术,可以把用户空间的内存和内核空间的内存映射到同一个物理地址上。这样DMA可以同时把数据写入到用户空间的缓冲区和内核空间缓冲区。后面介绍的NIO “直接缓存区“的概念跟它相关。
发散和聚集
许多操作系统能把组装/分解过程进行得更加高效。根据发散/汇聚的概念,进程只需一个系统调用,就能把一连串缓冲区地址传递给操作系统。然后,内核就可以顺序填充或排干多个缓冲区,读的时候就把数据发散到多个用户空间缓冲区,写的时候再从多个缓冲区把数据汇聚起来这样用户进程就不必多次执行系统调用(那样做可能代价不菲),内核也可以优化数据的处理过程,因为它已掌握待传输数据的全部信息。如果系统配有多个 CPU,甚至可以同时填充或排干多个缓冲区。这个知识点和NIO的特性“分散/聚集 I/O“相关。
二、阻塞和非阻塞、同步和异步
阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态采取的不同方式,说白了是一种读取或者写入操作函数的实现方式。阻塞方式下读取或者写入函数将一直等待;而非阻塞方式下,读取或者写入函数会立即返回一个状态值,继续执行后面代码。
同步和异步是针对应用程序和内核的交互而言的。同步指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪;而异步是指用户进程触发IO操作以后便开始做自己的事情,而当IO操作已经完成的时候会得到IO完成的通知。
所以I/O模型一般分为同步阻塞I/O、同步非阻塞I/O,异步阻塞I/O,异步非阻塞I/O。
同步阻塞IO:
在此种方式下,用户进程在发起一个IO操作后,用户进程hold住,等待用户空间的IO操作完成,只有当真正完成了IO操作以后,用户进程才会继续运行。JAVA传统的IO模型属于此种方式。可以参照前面的I/O模型的用户态和内核态理解。
同步非阻塞IO:
在此种方式下,用户进程发起一个IO请求以后,就可以往下继续执行,但是用户进程需要时不时地询问IO操作是否就绪。用户进程不间断的轮训,中间存在的一些进程上下文切换,会导致不必要的CPU资源浪费。目前NIO中较常用1.0的特性就属于同步非阻塞IO。
异步阻塞IO:
此种方式下是指用户进程发起一个IO操作以后,不等待内核IO操作的完成,等内核完成IO操作以后会通知应用程序,这其实就是同步和异步最关键的区别,同步必须等待或者主动的去询问IO是否完成,那么为什么说是阻塞的呢?因为此时是通过select系统调用来完成的,而select函数本身的实现方式是阻塞的,而采用select函数有个好处就是它可以同时监听多个文件句柄,从而提高系统的并发性。
异步非阻塞IO:
在此种模式下,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,用户进程会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。目前NIO 2.0的异步NIO支持此种IO模型。
关于Linux系统的select,poll,epoll系统函数介绍,见LINUX多路复用select,poll,epoll 。JDK NIO在不同操作系统下的native实现不同,但是了解了Linux系统,基本掌握了精髓。
三、Reactor和Proactor模式
Reactor和Proactor是IO多路复用模式.一般地,I/O多路复用机制都依赖于一个事件多路分离器(Event Demultiplexer)。分离器对象可将来自事件源的I/O事件分离出来,并分发到对应的read/write事件处理器(Event Handler)。开发人员预先注册需要处理的事件及其事件处理器(或回调函数)。
Reactor模式采用同步IO,而Proactor采用异步IO。
reactor |
preactor |
1. 应用程序注册读/写就绪事件和相关联的事件处理器 2. 事件分离器等待事件的发生 (Reactor负责) 3. 当发生读就绪事件的时候,事件分离器调用第一步注册的事件处理器(Reactor负责) 4. 事件处理器首先执行实际的读取操作,然后根据读取到的内容进行进一步的处理(用户处理器负责) |
1. 应用程序初始化一个异步读取操作,然后注册相应的事件处理器,此时事件处理器不关注读取就绪事件,而是关注读取完成事件,这是区别于Reactor的关键。 2. 事件分离器等待读取操作完成事件 3. 在事件分离器等待读取操作完成的时候,操作系统调用内核线程完成读取操作(异步IO都是操作系统负责将数据读写到应用传递进来的缓冲区供应用程序操作,操作系统扮演了重要角色),并将读取的内容放入用户传递过来的缓存区中。这也是区别于Reactor的一点,Proactor中,应用程序需要传递缓存区。 4. 事件分离器捕获到读取完成事件后,激活应用程序注册的事件处理器,事件处理器直接从缓存区读取数据,而不需要进行实际的读取操作。 |
四、NIO入门
developerwork上有一篇文章,易懂、详细且有示例代码,看完就基本就入门NIO了,url:http://www.ibm.com/developerworks/cn/education/java/j-nio/index.html,这里就不再造轮子了。
五、NIO深入
关于Select机制的原理可以通过陈皓(CoolShell博主)的两篇写的蛮有意思的文章来理解。
Java NIO类库Selector机制解析(上、下)
http://haoel.blog.51cto.com/313033/124582
http://haoel.blog.51cto.com/313033/124578
刚开始学习NIO的时候,在服务器写数据的时候,看到一段关于这样的IO写代码:
1. while (keyIterator.hasNext()) {
2. SelectionKey key = keyIterator.next();
3. keyIterator.remove();
4. if (key.isConnectable()) {
5. sc.finishConnect();
6. sc.register(selector, SelectionKey.OP_WRITE);
7. System.out.println("server connected...");
8. break;
9. } else if (key.isWritable()) {
10.
11. System.out.println("please input message");
12. String message = scanner.nextLine();
13. ByteBuffer writeBuffer = ByteBuffer.wrap(message.getBytes());
14. sc.write(writeBuffer);
15. }
16. }
其中关于SocketChannel的write()方法,为什么还要采用reactor模式进行写而大感不解,这个方法不是非阻塞的吗?只要往里面写不ok了。后来才发现自己too simple,too naive。具体为什么需要reactor模式,参照java nio 如何处理慢速的连接 。
六、NIO2 异步IO
网上大部分的关于NIO的文章,到同步非阻塞模式就嘎然而止,根本不提NIO2的异步非阻塞模式,而这样的文章被一而再,再而三的转载,却把最重要的东西湮没了,所以博文适时的更新是何其重要。关于此段内容,IBM的两位工程师提供了两篇详尽的文章,比较不错。
NIO.2 入门,第 1 部分: 异步通道 API
NIO.2 入门,第 2 部分: 文件系统 API
读完之后,你会觉得异步非阻塞模型确实很棒,这个才是我们最想要的I/O模型。
七、NIO开源框架
目前有较多的NIO开源框架,其中较出名的有MINA、CXF、Mule、JBoss/Geronimo,Grizzly,Cindy。有志者请自挖,这里不表。
八、总结
这里的总结非是以技术论断结尾,而是以学习NIO的人文关怀结尾。Java开发者一味关注的基本是类库的API,但是对于系统调用层级的理解,跟c和c++开发者,只能望其项背了。但是有JDK的新特性,无不是对于通过底层调用的优化来实现性能飞跃。这么看来,《Linux编程艺术》系列的书籍一直是被Java开发者误认的“鸡肋”罢了!
参考引用:
http://xmuzyq.iteye.com/blog/783218
http://developer.51cto.com/art/201112/307728.htm
http://developer.51cto.com/art/201112/307671.htm
相关推荐
通过深入学习和理解Selector机制,开发者可以有效地设计和实现高性能的服务器应用,比如网络服务器、聊天服务器等,充分利用系统资源,提高并发处理能力。结合实际项目需求,合理运用Java NIO的Selector机制,可以...
### Mina2.0学习笔记核心知识点概览 #### 一、Mina入门与环境搭建 ...以上是对《Mina2.0学习笔记(修订版)》的核心知识点概括,希望能帮助读者快速了解Mina框架的关键技术和应用场景,为后续深入学习打下坚实基础。
├─nio ├─security ├─sql ├─text ├─time └─util 阅读工具 Intellij IDEA Structure View:用于查看类的方法签名 Diagrams -> Show Diagrams 用于查看类的继承结构 CopyTranslator:用于复制英文注释进行...
Netty是一个高性能的异步事件驱动的网络应用框架,用于快速开发可维护的高性能协议服务器和客户端。Netty基于Java NIO(New...因此,无论对于新手还是有经验的开发者,Netty都是一个值得深入学习和使用的网络编程框架。
3. **IO/NIO**:Java输入输出系统,包括流的概念、缓冲区、文件操作,以及非阻塞I/O(New IO或NIO.2)的使用。 4. **多线程**:线程的创建、同步机制(synchronized、Lock)、并发工具类(ExecutorService、...
5. **IO流与NIO**:Java的输入输出(IO)流和非阻塞IO(NIO)用于读写文件、网络通信等,是处理数据传输的关键。书中会讲解流的分类、缓冲区以及通道的概念。 6. **多线程**:Java提供丰富的多线程支持,包括Thread...
由于缺少可读和可理解的内容,我们无法对这部分进行详细解释或概括。 不过,我们可以根据标题和描述提供的信息,来介绍与“Java核心技术卷二”相关的知识点。在Java核心技术系列书籍中,通常第二卷会着重介绍Java...
3. **IO与NIO**:Java的IO系统提供了丰富的输入输出操作,而NIO(New IO)引入了非阻塞I/O,提供了更好的性能和更高的并发性。这部分可能讲解了流的概念,缓冲区,通道,选择器,以及文件系统操作。 4. **反射**:...
由于CHM格式文件本身无法直接在此环境中展示,本文将根据描述和部分内容提取出关键知识点,以便于理解和学习。 #### 二、Java平台标准版5.0概览 Java平台标准版5.0(Java 2 Platform Standard Edition 5.0,简称...
推荐《Java_TCPIP_Socket网络编程.pdf》及《Java网络编程》书籍,重点了解NIO模型及其在mina、netty等流行框架中的应用。亲自实践网络通信编程是理解Dubbo网络通信不可或缺的步骤。 4. Java RPC机制:Dubbo作为一个...
虽然具体的页面内容无法在此直接展示,但我可以概括一下Java中常见的文件写入方法: 1. **FileWriter**:这是最基础的文件写入类,用于字符流输出。`FileWriter`可以追加或覆盖文件内容。例如: ```java ...
以上内容只是对《Java 实效编程百例》可能涵盖的主题的概括,实际书籍中可能会有更深入的讨论和示例,帮助读者掌握Java编程的精髓。文件www_sj00_com.txt和Javasjoxoambao可能包含了这些主题的具体实现或实战案例,...
抽象是对一类对象的共同特征的概括。在Java中,抽象类使用abstract关键字定义,不能直接实例化,只能被子类继承。抽象类可以包含抽象方法,这些方法没有具体实现,必须由子类提供。抽象类和抽象方法是实现多态的关键...
7. **IO和NIO**:Java提供了两种I/O模型,传统的IO基于字节流和字符流,NIO(非阻塞I/O)更适用于高并发场景。 8. **多线程**:Java内置了对多线程的支持,可以创建Thread对象或实现Runnable接口来实现并发执行。 ...
同时,标签通常是关键词,用于概括文件内容,例如“编程”,“数据库”,“网络技术”等。而“subject-main”可能是子文件夹或文件的名称,可能涉及到具体的知识点,如“主程序”或“核心代码”。 假设这是一个关于...
以上内容仅为《Java 2 参考大全》的部分精华概括,全书还涉及更多的主题,如反射、枚举、并发编程、NIO等。这本教材不仅适合Java初学者,也对有一定经验的开发者提供了丰富的参考资料,有助于提升Java编程技能。
描述部分简短地概括了这个项目是一个“简单数据库”,并再次强调是与 Netty 框架一起开发的,且项目尚未完成。这暗示该项目可能是在探索如何利用 Netty 实现一个轻量级、高性能的数据库连接或者数据传输机制,但并不...
【DZ_11】项目概述 在"Java"领域,DZ_11很可能是一个...以上是对DZ_11项目可能涉及的Java知识点的概括,具体项目中的实现和技术栈还需查看源代码才能深入理解。在实际学习或使用时,应结合代码和文档进行详细研究。
描述中的"IdeaProjects"可能是对这个压缩包内容的简洁概括,暗示了它可能包含了一系列使用IntelliJ IDEA创建或管理的项目。这些项目可能是教学示例,开源代码库,或者是某个特定技术或框架的实践应用。 标签"Java...