本系列的上一篇文章已经介绍过,IO的阻塞与非阻塞看的是发起IO请求时是否会被阻塞。
一、使用阻塞IO:
在原来进行Java Socket网络编程时,每打开一个I/O通道,read()就一直等待读取字节内容,如果内容没有准备好,read()会阻塞直到数据到来,但此时线程不能做其它事情,所以解决方法就是开辟一个线程池,把每个请求分发到一个线程中去,让线程去等待。
存在的问题:
一个客户端一个线程的方式去处理,则由于创建、维护和切换线程需要的系统开销导致系统扩展性方面受到了很大限制。对于连接生存期比较长的协议来说,线程池的大小仍然限制了系统可以同时处理的客户端数量。如果增加线程池的大小,将带来更多的线程处理开销,而不能提升系统的性能,因为在大部分的时间里客户端是处于空闲状态的。
二、对阻塞IO的改进:
根据上文中的情况,由于要一直等待数据准备好,所以进程会一直阻塞直到有数据进来。因此能不能在有数据进来时自动通知,这样就不必开启多个线程死等,从而也就不堵塞了。
其实这就是NIO中就使用的一种多线程模式reactor(有些文章翻译成反应器或反应堆模式)。
三、关于Reactor模式:
Reactor:负责响应IO事件,当检测到一个新的事件,将其发送给相应的Handler去处理。
Reactor在Java NIO中由JDK类库提供的Selector类实现,循环监听所有注册到Selector上Channel的事件,在没有感兴趣的事件时阻塞。
事件类型及对应常量值:
读事件:SelectionKey.OP_READ(1)
写事件:SelectionKey.OP_WRITE(4)
客户端连接服务端事件:SelectionKey.OP_CONNECT(8)
服务端接收客户端连接事件:SelectionKey.OP_ACCEPT(16)
Acceptor:负责接受客户端连接,并实现分派任务操作处理。
Handler:负责处理请求(read...send),同时将handler与事件绑定。
关于Reactor模式,这里有一个关于服务员处理顾客点餐的比喻,我觉得很生动形象,看过后应该更容易理解,分享给大家:http://daimojingdeyu.iteye.com/blog/828696
四、Java NIO:
Java BIO中,一直使用流的方式完成I/O。所有I/O都被视为单个的字节移动,通过Stream对象一次移动一个字节。
Java NIO使用不同的方式--块I/O,块I/O的效率可以比流I/O高许多。
面向流的I/O一次一个字节地处理数据。一个输入流产生一个字节的数据,一个输出流消费一个字节的数据。面向流的I/O通常相当慢。
面向块的I/O以块的形式处理数据。每一个操作都在一步中产生或者消费一个数据块。按块处理数据比按流式的字节处理数据要快得多。但是面向块的I/O缺少一些面向流的 I/O 所具有的优雅性和简单性。
1、缓冲区:
Java NIO 中,所有数据都是用缓冲区处理的。
在读取数据时,从通道中读取的任何数据是直接读到缓冲区中的。
在写入数据时,发送给通道的所有对象都必须首先放到缓冲区中。
缓冲区实质上是一个数组,但缓冲区不仅仅是一个数组。缓冲区提供了对数据的结构化访问,而且还可以跟踪系统的读/写进程
(1)缓冲区分配和包装:
在能读写之前,必须有一个缓冲区。要创建缓冲区,您必须分配它。我们使用静态方法allocate()来分配缓冲区,值得注意的是 Buffer 及其子类都不是线程安全的。
ByteBuffer buffer = ByteBuffer.allocate(1024);
将一个现有的数组转换为缓冲区:
byte array[] = new byte[1024];
ByteBuffer buffer = ByteBuffer.wrap(array);
slice()方法根据现有的缓冲区创建子缓冲区
buffer.position(3);
buffer.limit(7);
ByteBuffer slice = buffer.slice();
新缓冲区与原来的缓冲区共享一部分数据,如果修改子缓冲区中的数据,原缓冲区内对应数据也会被修改。
(2)只读缓冲区
可以读取它们,但是不能向它们写入。通过调用缓冲区的asReadOnlyBuffer()方法,将任何常规缓冲区转换为只读缓冲区。
(3)直接缓冲区
为加快I/O速度,而以一种特殊的方式分配其内存的缓冲区。
Sun文档:
给定一个直接字节缓冲区,Java虚拟机将尽最大努力直接对它执行本机I/O操作。
也就是说,它会在每一次调用底层操作系统的本机I/O操作之前(或之后),尝试避免将缓冲区的内容拷贝到一个中间缓冲区中(或者从一个中间缓冲区中拷贝数据)。
(4)内存映射文件I/O:
只有文件中实际读取或者写入的部分才会送入或者映射到内存中。只是改变数组的单个元素这样的简单操作,就可能会直接修改磁盘上的文件。
MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE,0,1024);
将文件的前1024个字节映射到内存。
2、Channel:
Channel是一个对象,可以通过它读取和写入数据。通道就像是流。
将数据写入包含一个或者多个字节的缓冲区,不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。
通道与流的不同之处在于通道是双向的,而流只是在一个方向上移动(一个流必须是InputStream或者OutputStream的子类),而通道可以用于读、写或者同时用于读写。
3、状态变量:
(1)、三个值指定缓冲区在任意时刻的状态:
position:跟踪已经写了或者读了多少数据。
limit:表明还有多少数据需要取出(在从缓冲区写入通道时),或者还有多少空间可以放入数据(在从通道读入缓冲区时)。position总是小于或者等于limit。
capacity:表明可以储存在缓冲区中的最大数据容量.limit决不能大于capacity。
另外,关于mark:
一个临时存放的位置下标。调用 mark() 会将mark设为当前的position的值,以后调用reset()会将position属性设置为mark的值。 mark的值总是小于等于position的值,如果将position的值设的比mark小,当前的mark值会被抛弃掉。
(2)、访问方法:
ByteBuffer.get() 获取字节
ByteBuffer.put() 写入字节
Buffer.clear()
重设缓冲区,使它可以接受读入的数据。它将limit设置为与capacity相同。设置position为0。
Buffer.flip()
让缓冲区可以将新读入的数据写入另一个通道。它limit设置为当前position,将position设置为0。
4、Selector、SelectableChannel和SelectionKey:
SelectableChannel:
代表了可以支持非阻塞IO操作的channel,可以将其注册在Selector上,这种注册的关系由SelectionKey这个类来表现。
SelectableChannel可以是blocking和non-blocking模式,所有channel创建的时候都是blocking模式,只有 non-blocking的SelectableChannel才可以参与非阻塞IO操作。
通过register()方法,SelectableChannel可以注册到Selector上。
ServerSocketChannel支持非阻塞操作,对应于java.net.ServerSocket这个类,提供了TCP协议IO接口,支持OP_ACCEPT操作。
socket():返回对应的ServerSocket对象。
accept():接受一个连接,返回代表这个连接的SocketChannel对象。
SocketChannel支持非阻塞操作,对应于java.net.Socket这个类,提供了TCP协议IO接口,支持OP_CONNECT,OP_READ和OP_WRITE操作。
socket():返回对应的Socket对象。
finishConnect():connect()进行一个连接操作。如果当前SocketChannel是blocking模式,这个函数会等到连接操作完成或错误发生才返回。如果当前SocketChannel是non-blocking模式,函数在连接能立刻被建立时返回true,否则函数返回false,应用程序需要在以后用finishConnect()方法来完成连接操作。
Selector:
这个类通过select() 函数,给应用程序提供了一个可以同时监控多个IO channel的方法。
应用程序通过调用select() 函数,让Selector监控注册在其上的多个SelectableChannel ,当有channel的IO操作可以进行时,select()方法就会返回以让应用程序检查channel的状态,并作相应的处理。
Selector可以同时监控多个SelectableChannel的IO状况,是非阻塞IO的核心:
在一个Selector 中,有3个SelectionKey的集合:
(1)key set代表了所有注册在这个Selector上的channel ,这个集合可以通过keys()方法拿到。
(2)Selected-key set代表了所有通过select()方法监测到可以进行IO操作的channel ,这个集合可以通过 selectedKeys()拿到。
(3)Cancelled-key set代表了已经cancel了注册关系的channel ,在下一个select()操作中,这些channel对应的SelectionKey会从key set和cancelled-key set中移走,这个集合无法直接访问。
5、其它API:
Pipe:
包含了一个读和一个写的channel(Pipe.SourceChannel 和 Pipe.SinkChannel) ,这对channel可以用于进程中的通讯。
FileChannel:
用于对文件的读、写、映射、锁定等操作,和映射操作相关的类有FileChannel.MapMode,和锁定操作相关的类有FileLock。值得注意的是FileChannel并不支持非阻塞操作。
Channels:
这个类提供了一系列static方法来支持stream类和channel类之间的互操作。这些方法可以将channel类包装为 stream类,比如,将ReadableByteChannel包装为InputStream或Reader;也可以将stream类包装为channel 类,将OutputStream包装为WritableByteChannel。
相关推荐
与之相比,传统IO服务器模型在处理并发流量时,不仅性能上无法达到新模型的水平,而且服务器负载通常会较高。这些数据证明了Java NIO在高并发处理上的优越性。 总结来说,Java NIO通过其非阻塞特性、多路复用能力...
Java NIO:浅析IO模型 Java NIO是Java语言中用于高性能I/O操作的API,理解IO模型是学习Java NIO的基础。本文将从同步和异步的概念开始,然后介绍阻塞和非阻塞的区别,接着介绍阻塞IO和非阻塞IO的区别,最后介绍五种...
#### 二、Java NIO 与传统 IO 的主要区别 Java NIO 相对于传统 IO 的主要区别在于: 1. **面向缓冲区而非流**:在传统 IO 中,数据被读取或写入流。而在 NIO 中,所有数据都通过缓冲区(Buffer)进行操作。 2. **...
综上所述,基于Java NIO的反应器模式设计与实现,可以大幅提升网络服务器的性能,通过非阻塞IO、事件驱动、选择器等机制,高效地处理高并发的数据传输任务,并且优化了线程资源的使用,减少了线程上下文切换的时间...
《Java IO:从NIO到Reactor三种模式详解》 在Java编程中,IO操作是不可或缺的一部分,尤其在处理大量数据传输或者网络通信时。本文将深入探讨Java中的三种IO模型:传统IO(BIO)、非阻塞IO(NIO)以及反应器模式...
Java NIO 概述与实现详解 Java NIO(New IO 或 Non Blocking IO)是从 Java 1.4 版本开始引入的一个新的IO API,可以替代标准的 Java IO API。NIO 支持面向缓冲区的、基于通道的 IO 操作。NIO 将以更加高效的方式...
在深入探讨《Scalable IO in Java》的中文版内容之前,首先需要了解Java中的I/O模型发展历程及其在服务器编程中的重要性。在Java中,I/O处理经历了从传统的BIO(阻塞I/O),到NIO(非阻塞I/O),再到AIO(异步I/O)...
本文主要简单介绍NIO的基本原理,在下一篇文章中,将结合Reactor模式和著名线程大师Doug Lea的一篇文章深入讨论。 NIO主要原理和适用。 NIO 有一个主要的类Selector,这个类似一个观察者,只要我们把需要探知的...
总结来说,《Scalable IO in Java》文档是Java开发者提升I/O处理能力的重要参考资料,它涵盖了Reactor模式的多种实现,以及Java NIO库在其中的角色。通过深入学习和实践,我们可以更好地理解和利用这些技术,优化...
本项目深入探讨了Java网络编程中的多种模式,包括BIO(阻塞IO)、NIO(非阻塞IO)、IO多路复用(select、poll、epoll)、Reactor模式,以及零拷贝技术。通过这些实现,项目展示了如何在高并发环境下优化网络通信效率...
总之,Scalable IO in Java涉及到使用`java.nio`库实现非阻塞IO和Reactor模式,以实现高效的网络服务处理,适应高并发场景,并充分利用系统资源。理解并熟练运用这些技术对于构建可扩展的现代网络应用至关重要。
Doug Lea在书中详细阐述了Java NIO(New IO)框架,这是Java平台提供的一种用于实现Scalable IO的关键工具。NIO引入了通道(Channels)和缓冲区(Buffers)的概念,它们允许数据在不同实体之间以非阻塞方式传输。...
### Scalable IO in Java:详解Java NIO与网络服务扩展性设计 #### 一、概述 在《Scalable IO in Java》这篇文章中,作者Doug Lea深入探讨了如何利用Java NIO(非阻塞I/O)技术实现可扩展的网络服务。随着互联网...
Java NIO(New Input/Output)网络框架是Java平台中用于高效处理I/O操作的一种机制。相较于传统的IO模型,NIO具有非阻塞、多路复用的特点,使其在高并发场景下表现优异,尤其适合于开发网络服务,如服务器应用、聊天...
### NIO陷阱与解读 #### 一、NIO概述及变迁 **NIO**(New I/O)是Java为提高I/O操作效率而引入的新特性,最初在JDK 1.4版本中作为JSR 51规范的一部分被提出。随着技术的发展,在JDK 7中进一步扩展为NIO 2(JSR 203...
标题《Scalable IO in Java》和描述表明本文档讨论了Java中的可伸缩网络I/O编程,并且特别聚焦于NIO的Selector模式。NIO,全称为Non-blocking I/O(非阻塞I/O),是Java提供的一种用于处理网络通信或文件I/O的编程...
第35讲:Java NIO核心类源码解读与分析 第36讲:文件通道用法详解 第37讲:Buffer深入详解 第38讲:NIO堆外内存与零拷贝深入讲解 第39讲:NIO中Scattering与Gathering深度解析 第40讲:Selector源码深入分析 ...
### NIO Trick and Trap:构建高性能Java NIO网络框架 #### 概述 NIO(New I/O),作为Java平台的一项重要技术革新,为开发者提供了更高效的数据处理方式。相较于传统的IO模型,NIO通过非阻塞式I/O操作、多路复用...