`
Mojarra
  • 浏览: 130769 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

Java NIO小结 (二)

阅读更多

 

除了第一篇小结中讲的Java New/IO的几个基本新特性外,New I/O中一个最突出的特性就是Non-blocking I/O了,这个特性是针对原java.net包中socket编程的一个极大的补充和拓展。究竟non-blocking如何使用?有何特点?与socket的blocking IO相比,有哪些优势?

 

JDK1.4 以前,在调用 ServerSocket.accept() 方法等待一个连接入的套接字时,该方法一直是等待的,直到有客户端的套接字接入才返回一个套接字,或者抛出 IOException 。套接字读取输入流时到缓存中的时候,必须等到套接字输入流的输入数据是可用的,或者出现异常,或者读取输入流的末尾。试想,假如客户端套接字发送报文给服务端套接字的过程中,因网络的问题,数据没有发送过来或者有延迟,那么服务器端套接字在读取数据时,会一直处于等待状态。常见的框架代码如下,

 

ServerSocket server = new ServerSocket(1299);
while (true) {
	 Socket socket = server.accept();
	 (new RequestHandlerThread(socket)).start();
}
 

 

在面向线程的阻塞 IO 编程模式下, ServerSocket 接受到一个套接字后,会启动一个线程负责套接字之间的通讯,但是,如果出现 IO 阻塞,这个线程也会处于“阻塞”状态,如果 IO 阻塞没有被及时排除,会引起僵尸线程,甚至整个系统不可用。

 

而在多路复用 Non-blocking 模式下, socket 编程采用事件模式,主要用 Selector 来读取感兴趣的事件,用 Channel 注册感兴趣的事件和负责发送、接收数据, SelectionKey 是对事件的封装。这种编程模式的框架代码如下,

 

// 打开selctor和服务器端SocketChannel,并且配置为non-blocking模式,然后绑定到9000端口。 
Selector sel = Selector.open();
 ServerSocketChannel ssc = ServerSocketChannel.open();
 ssc.configureBlocking(false);
 ssc.socket().bind(new InetSocketAddress(9000));

// 注册OP_ACCEPT事件,让selector在下一次select操作中选择OP_ACCEPT事件
 ssc.register(sel, SelectionKey.OP_ACCEPT);

while (selector.select()>0){
                Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
		while (keys.hasNext()) {
			SelectionKey key = keys.next();
			keys.remove();
			if (key.isValid()) {
				if (key.isAcceptable()) {
					 // ....   接受SocketChannel,并注册OP_READ事件
				} else if (key.isReadable()) {
					// ... 从SocketChannel读取数据
				}
				else if (key.isWritable()){
					 // ... 向SocketChannel 写入数据
				}
                              // 把当前已处理的key放入到selector的cancelling selectionkey集合当中,在下一次select操作中,该key被移去。 
                              key.cancel();
			}
		}
}
 

 

Selector 是包含多个 Selectable Channel 的转接器,这些通道可以设置成非阻塞模式去执行多路 I/O 操作。这些通道必须先被创建,然后设置成非阻塞模式,并注册到 Selector 之中。注册通道时指定一些特定的 I/O 操作,这些 I/O 操作将会为 selector 所检测,并返回一个代表这个注册的 SelectionKey

 

一旦通道被注册到一个 Selector, 选择操作才可以执行去发现有没有通道已经处于可以执行这些先前被生命注册 I/O 操作的状态。如果一个通道已经就绪,返回一个 SelectionKey 并把这个 key 添加到 Selector Selected-Key 集。在下一次选择操作过程中,会把这个带有相关通道 key 检索出来去执行相关的 I/O 操作。

 

一个 SelectionKey 标明一个通道可以准备执行什么操作,仅仅是一个提示,而不是保证。因此,这个操作可以放到一个线程中去执行,而不会引起线程阻塞。这对于编写多路复用 I/O 的代码是非常重要的,假使这个提示在后来被证明是不正确的,也可以在代码中忽略掉它。

 

Selector 有三个主要的方法,

Select() 以阻塞的方式从当前的 selector 中选取那些已经准备好 I/O 操作 Channel key ,至少有一个 channel 被选取后才返回,或者 weekup 被调用,或者当前线程被终止执行。

 

selectNow() ,与 select() 方法类似,只不过是非阻塞的,如果当前 selector 中没有已准备 I/O 操作的 key ,该方法立即返回 0 。之前如有 weekup() 被调用, SelectNow() 会清除掉前者的执行效果。

 

weekup() ,让第一个还没有返回的选择操作立即返回,如果另外一个线程被 select() 或者 select(long) 方法调用阻止,调用 weekup() 方法,会让 select() 或者 select(long) 调用立即返回。如果当前进程中没有选择操作,会导致下轮 select(), select(long) 方法调用立即返回,除非同时有 selectNow() 方法被调用。在两此成功的选择操作过程中间调用多次 weekup() 方法与只调用一次的效果是相同的。

 

Non-blocking 模式下,用 Channel 读取输入缓冲中的数据时,读取到的字节数依赖于当前 Channel 的状态。比如下面的代码。

ByteBuffer buff = ByteBuffer.allocate(128);

socketChannel.read(buff);
 

 

如果 Channel 缓冲区没有数据,那么读取了 0 字节,如果 Channel 的缓冲区里有 50 个“立即可读取”字节,则读取 50 个字节,假如已经到了流的末尾,则返回 -1 。因为 Channel 是直接从缓冲区读取数据,而不是从流读取,所以, Channel 的这种工作方式效率比从流直接读取效率高。

Channel 写数据的时候,也是先写入 Channel 的写入缓冲区,而不是直接写入 Channel 的输出流。具体写入的字节数由 Channel 的输出缓冲区的状态决定。考察下面的代码。

 

byte out[] = new byte[256];
for (int i=0; i<256; i++) 
     out[i]=i;

socketChannel.write(ByteBuffer.wrap(out));
 

 

假如输出缓冲区还剩下 50 个空余字节,那么本次写入操作只能写 50 个字节。如果输出缓冲区剩余 500 个空余字节,则能写入 256 个字节。

 

至于 Channel 的从输入流读数据到输入缓冲,什么时候把输出缓冲写入到输出流,由 Channle 的实现细节决定。以后会对 Channel 中的缓冲于流的机制做深入的研究。使用 Channel 时,一定要记住,虽然看起来和 InputStream OutputStream 使用方式看起来一样,但其内部机制是有天壤之别的。

 

末尾,以表格的形式对 socket 编程中用到的主要方法的阻塞特性作一个小结。

ServerSocketChannel.accept()

非阻塞模式 / 阻塞模式

ServerSocket.accept()

阻塞模式

Socket input stream read

阻塞模式

Socket output stream write

阻塞模式

Channel input Buffer read

非阻塞模式 / 阻塞模式

Channel output buffer write

非阻塞模式 / 阻塞模式

Selector.select()

阻塞模式

Selector.select(long l)

阻塞模式

Selector.selectNow()

非阻塞模式 / 阻塞模式

 

分享到:
评论

相关推荐

    Java 垃圾回收小结(一)

    本文将对Java垃圾回收进行小结,探讨其基本原理、类型以及常见算法。 1. 基本原理: Java中的内存分为堆(Heap)和栈(Stack)两部分,垃圾回收主要关注堆内存。当一个对象不再被任何引用指向时,它被视为可回收的...

    java线程与并行(主要讲解java的nio包某些内容)

    #### 五、小结 本文详细介绍了Java中线程的基本概念、创建方式、生命周期以及如何控制线程执行等知识点。通过实际示例代码展示了如何创建和使用线程,帮助读者更好地理解和应用Java线程技术。在实际开发中,合理...

    Java下TCP文件传输功能实现

    ### 小结 本文介绍了 Java 中如何利用 TCP 协议实现文件传输。通过了解 TCP/IP 协议的基础知识以及 Socket 编程的基本原理,我们实现了客户端与服务器之间的文件传输功能。这种技术在实际开发中非常有用,尤其是在...

    java 实现音乐播放器资料总结

    此外,可能还需要用到java.nio.file包中的类,以支持更高级别的文件系统操作。 7. **播放列表管理**:音乐播放器通常包含播放列表功能,允许用户添加、删除和排序歌曲。这需要数据结构如ArrayList或LinkedList来...

    Java 网络编程实验报告 含实验总结

    在这个实验中,可能使用了`java.io`和`java.nio`包下的类,如`InputStream`、`OutputStream`、`BufferedReader`和`PrintWriter`等,它们用于在网络连接中接收和发送数据。 2. **Java线程**:在网络编程中,多线程是...

    java初学者应该阅读的书籍

    ### 小结 通过系统地学习上述书籍,Java初学者将能够建立起全面而深入的知识体系。这些书籍不仅仅是理论知识的堆砌,更重要的是提供了大量实用的代码示例和案例分析,有助于读者更好地理解和掌握Java语言的特点。...

    基于netty的websocket开发小结

    这篇基于Netty的WebSocket开发小结将探讨如何使用Netty实现WebSocket服务端和客户端的交互。 首先,我们要理解WebSocket的基本概念。WebSocket协议定义了一种在单个TCP连接上进行全双工通信的协议。它通过在握手...

    疯狂JAVA讲义

    1.9 本章小结 22 本章练习 22 第2章 理解面向对象 23 2.1 面向对象 24 2.1.1 结构化程序设计简介 24 2.1.2 程序的三种基本结构 25 2.1.3 面向对象程序设计简介 27 2.1.4 面向对象的基本特征 28 2.2 UML...

    Java SE实践教程 源代码 下载

    1.3 小结 35 第2章 对象无处不在——面向对象的基本概念 37 2.1 讲解 38 2.1.1 什么是面向对象 38 2.1.2 面向对象的基本概念 38 2.1.3 Java对面向对象的支持 41 2.2 练习 42 2.2.1 JavaBeans技术开发可重用...

    Java SE实践教程 pdf格式电子书 下载(四) 更新

    Java SE实践教程 pdf格式电子书 下载(二) 更新 http://download.csdn.net/source/2824040 Java SE实践教程 pdf格式电子书 下载(三) 更新 http://download.csdn.net/source/2824042 Java SE实践教程 pdf格式...

    Java SE实践教程 pdf格式电子书 下载(一) 更新

    Java SE实践教程 pdf格式电子书 下载(二) 更新 http://download.csdn.net/source/2824040 Java SE实践教程 pdf格式电子书 下载(三) 更新 http://download.csdn.net/source/2824042 Java SE实践教程 pdf格式...

    java实现守护进程,有单独的监听进程, 两个或多个进程,两个或多个jvm

    在Java中,我们可以使用`java.net.ServerSocket`来监听TCP连接,`java.nio.channels.FileChannel`来监听文件系统变化。监听进程通常设计为无阻塞的,以确保它可以高效地处理多个并发事件。 接下来,我们将探讨如何...

    java结课设计,一款完全基于servlet的网盘系统.zip

    4. **文件I/O操作**: Java提供了一系列的类库支持文件的读写操作,如java.io.File、java.nio.file.Files等,这些在网盘系统中必不可少,用于处理文件的上传和下载。 5. **HTTP协议**: 作为基于HTTP的系统,理解和...

    文件读写操作小结

    本文将结合标题“文件读写操作小结”和提供的标签“源码”、“工具”,深入探讨文件读写的核心概念、常见方法以及在实际应用中的注意事项。 1. 文件系统基础 文件系统是操作系统用于组织和管理磁盘上数据的一种方式...

    Tomcat6性能优化(小结).pdf

    Tomcat6是一款广泛使用的开源Java Servlet容器,主要用于部署Java Web应用程序。在优化Tomcat6的性能时,了解其内部工作原理以及如何调整配置至关重要。本篇文章将聚焦于Tomcat6的性能优化,特别是关于NIO(非阻塞I/...

    java Streams and Serialization 详解

    #### 小结 通过本文的学习,我们不仅深入了解了Java中流的基本概念、分类和使用方法,还掌握了序列化的原理和实现方式。无论是进行数据读写还是对象状态的保存与恢复,掌握流和序列化的相关知识都是Java开发者不可...

    Java+TCPIP+Socket编程(中文版)

    - **Selector小结** Selector的工作原理总结。 - **数据报(UDP)信道** UDP信道的使用方法。 #### 第6章 深入剖析 - **缓冲和TCP** 缓冲区的管理及其对TCP性能的影响。 - **死锁风险** 并发环境下死锁...

Global site tag (gtag.js) - Google Analytics