除了第一篇小结中讲的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垃圾回收进行小结,探讨其基本原理、类型以及常见算法。 1. 基本原理: Java中的内存分为堆(Heap)和栈(Stack)两部分,垃圾回收主要关注堆内存。当一个对象不再被任何引用指向时,它被视为可回收的...
#### 五、小结 本文详细介绍了Java中线程的基本概念、创建方式、生命周期以及如何控制线程执行等知识点。通过实际示例代码展示了如何创建和使用线程,帮助读者更好地理解和应用Java线程技术。在实际开发中,合理...
### 小结 本文介绍了 Java 中如何利用 TCP 协议实现文件传输。通过了解 TCP/IP 协议的基础知识以及 Socket 编程的基本原理,我们实现了客户端与服务器之间的文件传输功能。这种技术在实际开发中非常有用,尤其是在...
此外,可能还需要用到java.nio.file包中的类,以支持更高级别的文件系统操作。 7. **播放列表管理**:音乐播放器通常包含播放列表功能,允许用户添加、删除和排序歌曲。这需要数据结构如ArrayList或LinkedList来...
在这个实验中,可能使用了`java.io`和`java.nio`包下的类,如`InputStream`、`OutputStream`、`BufferedReader`和`PrintWriter`等,它们用于在网络连接中接收和发送数据。 2. **Java线程**:在网络编程中,多线程是...
### 小结 通过系统地学习上述书籍,Java初学者将能够建立起全面而深入的知识体系。这些书籍不仅仅是理论知识的堆砌,更重要的是提供了大量实用的代码示例和案例分析,有助于读者更好地理解和掌握Java语言的特点。...
这篇基于Netty的WebSocket开发小结将探讨如何使用Netty实现WebSocket服务端和客户端的交互。 首先,我们要理解WebSocket的基本概念。WebSocket协议定义了一种在单个TCP连接上进行全双工通信的协议。它通过在握手...
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...
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格式电子书 下载(二) 更新 http://download.csdn.net/source/2824040 Java SE实践教程 pdf格式电子书 下载(三) 更新 http://download.csdn.net/source/2824042 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中,我们可以使用`java.net.ServerSocket`来监听TCP连接,`java.nio.channels.FileChannel`来监听文件系统变化。监听进程通常设计为无阻塞的,以确保它可以高效地处理多个并发事件。 接下来,我们将探讨如何...
4. **文件I/O操作**: Java提供了一系列的类库支持文件的读写操作,如java.io.File、java.nio.file.Files等,这些在网盘系统中必不可少,用于处理文件的上传和下载。 5. **HTTP协议**: 作为基于HTTP的系统,理解和...
本文将结合标题“文件读写操作小结”和提供的标签“源码”、“工具”,深入探讨文件读写的核心概念、常见方法以及在实际应用中的注意事项。 1. 文件系统基础 文件系统是操作系统用于组织和管理磁盘上数据的一种方式...
Tomcat6是一款广泛使用的开源Java Servlet容器,主要用于部署Java Web应用程序。在优化Tomcat6的性能时,了解其内部工作原理以及如何调整配置至关重要。本篇文章将聚焦于Tomcat6的性能优化,特别是关于NIO(非阻塞I/...
#### 小结 通过本文的学习,我们不仅深入了解了Java中流的基本概念、分类和使用方法,还掌握了序列化的原理和实现方式。无论是进行数据读写还是对象状态的保存与恢复,掌握流和序列化的相关知识都是Java开发者不可...
- **Selector小结** Selector的工作原理总结。 - **数据报(UDP)信道** UDP信道的使用方法。 #### 第6章 深入剖析 - **缓冲和TCP** 缓冲区的管理及其对TCP性能的影响。 - **死锁风险** 并发环境下死锁...