转自:http://blog.csdn.net/halemyan/archive/2007/04/17/1567413.aspx
Java NIO非堵塞应用通常适用用在I/O读写等方面,我们知道,系统运行的性能瓶颈通常在I/O读写,包括对端口和文件的操作上,过去,在打开一个I/O通道后,read()将一直等待在端口一边读取字节内容,如果没有内容进来,read()也是傻傻的等,这会影响我们程序继续做其他事情,那么改进做法就是开设线程,让线程去等待,但是这样做也是相当耗费资源的。
Java NIO非堵塞技术实际是采取Reactor模式,或者说是Observer模式为我们监察I/O端口,如果有内容进来,会自动通知我们,这样,我们就不必开启多个线程死等,从外界看,实现了流畅的I/O读写,不堵塞了。
Java NIO出现不只是一个技术性能的提高,你会发现网络上到处在介绍它,因为它具有里程碑意义,从JDK1.4开始,Java开始提高性能相关的功能,从而使得Java在底层或者并行分布式计算等操作上已经可以和C或Perl等语言并驾齐驱。
如果你至今还是在怀疑Java的性能,说明你的思想和观念已经完全落伍了,Java一两年就应该用新的名词来定义。从JDK1.5开始又要提供关于线程、并发等新性能的支持,Java应用在游戏等适时领域方面的机会已经成熟,Java在稳定自己中间件地位后,开始蚕食传统C的领域。
原来的 I/O 库(在 java.io.*
中) 与 NIO 最重要的区别是数据打包和传输的方式。正如前面提到的,原来的 I/O 以流的方式处理数据,而 NIO 以块的方式处理数据。
面向流 的 I/O 系统一次一个字节地处理数据。一个输入流产生一个字节的数据,一个输出流消费一个字节的数据。为流式数据创建过滤器非常容易。链接几个过滤器,以便每个过滤器只负责单个复杂处理机制的一部分,这样也是相对简单的。不利的一面是,面向流的 I/O 通常相当慢。
一个 面向块 的 I/O 系统以块的形式处理数据。每一个操作都在一步中产生或者消费一个数据块。按块处理数据比按(流式的)字节处理数据要快得多。但是面向块的 I/O 缺少一些面向流的 I/O 所具有的优雅性和简单性。
public static void main(String [] args)
throws IOException
{
//
创建一个
capacity
为
256
的
ByteBuffer
ByteBuffer buf = ByteBuffer.allocate(256);
while (true) {
//
从标准输入流读入一个字符
int c = System.in.read();
//
当读到输入流结束时,退出循环
if (c == -1)
break;
//
把读入的字符写入
ByteBuffer
中
buf.put((byte) c);
//
当读完一行时,输出收集的字符
if (c == '\n') {
//
调用
flip()
使
limit
变为当前的
position
的值
,position
变为
0,
//
为接下来从
ByteBuffer
读取做准备
buf.flip();
//
构建一个
byte
数组
byte [] content = new byte[buf.limit()];
//
从
ByteBuffer
中读取数据到
byte
数组中
buf.get(content);
//
把
byte
数组的内容写到标准输出
System.out.print(new String(content));
//
调用
clear()
使
position
变为
0,limit
变为
capacity
的值,
//
为接下来写入数据到
ByteBuffer
中做准备
buf.clear();
}
}
}
|
private static void acceptConnections(int port) throws Exception {
//
打开一个
Selector
Selector acceptSelector =
SelectorProvider.provider().openSelector();
//
创建一个
ServerSocketChannel
,这是一个
SelectableChannel
的子类
ServerSocketChannel ssc = ServerSocketChannel.open();
//
将其设为
non-blocking
状态,这样才能进行非阻塞
IO
操作
ssc.configureBlocking(false);
//
给
ServerSocketChannel
对应的
socket
绑定
IP
和端口
InetAddress lh = InetAddress.getLocalHost();
InetSocketAddress isa = new InetSocketAddress(lh, port);
ssc.socket().bind(isa);
//
将
ServerSocketChannel
注册到
Selector
上,返回对应的
SelectionKey
SelectionKey acceptKey =
ssc.register(acceptSelector, SelectionKey.OP_ACCEPT);
int keysAdded = 0;
//
用
select()
函数来监控注册在
Selector
上的
SelectableChannel
//
返回值代表了有多少
channel
可以进行
IO
操作
(ready for IO)
while ((keysAdded = acceptSelector.select()) > 0) {
// selectedKeys()
返回一个
SelectionKey
的集合,
//
其中每个
SelectionKey
代表了一个可以进行
IO
操作的
channel
。
//
一个
ServerSocketChannel
可以进行
IO
操作意味着有新的
TCP
连接连入了
Set readyKeys = acceptSelector.selectedKeys();
Iterator i = readyKeys.iterator();
while (i.hasNext()) {
SelectionKey sk = (SelectionKey) i.next();
//
需要将处理过的
key
从
selectedKeys
这个集合中删除
i.remove();
//
从
SelectionKey
得到对应的
channel
ServerSocketChannel nextReady =
(ServerSocketChannel) sk.channel();
//
接受新的
TCP
连接
Socket s = nextReady.accept().socket();
//
把当前的时间写到这个新的
TCP
连接中
PrintWriter out =
new PrintWriter(s.getOutputStream(), true);
Date now = new Date();
out.println(now);
//
关闭连接
out.close();
}
}
}
|