- 浏览: 68495 次
- 性别:
- 来自: 武汉
最新评论
-
wanglantao:
谢谢了!!!!
ext中文API文档及例子 -
lilongsy:
问一下,phpcms和supesite哪个更好用?
我现在用 ...
phpcms转supesite转换程序bug修正 -
hwn0412:
辛苦了..刚好要用..
Ajax框架之ExtJs、DWR入门书籍 -
wkl17:
感谢楼主分享,但是step4修改了之后,进行转换,会提示
引用 ...
phpcms转supesite转换程序bug修正 -
Chen_ZX:
之前接触得比较少,现在有时间多学习一点。谢谢分享!
ext中文API文档及例子
在JDK 1.4的新特性中,NIO无疑是最显著和鼓舞人心的。NIO的出现事实上意味着Java虚拟机的性能比以前的版本有了较大的飞跃。在以前的JVM的版本中,代码的执行效率不高(在最原始的版本中Java是解释执行的语言),用Java编写的应用程序通常所消耗的主要资源就是CPU,也就是说应用系统的瓶颈是CPU的计算和运行能力。在不断更新的Java虚拟机版本中,通过动态编译技术使得Java代码执行的效率得到大幅度提高,几乎和操作系统的本地语言(例如C/C++)的程序不相上下。在这种情况下,应用系统的性能瓶颈就从CPU转移到IO操作了。尤其是服务器端的应用,大量的网络IO和磁盘IO的操作,使得IO数据等待的延迟成为影响性能的主要因素。NIO的出现使得Java应用程序能够更加紧密地结合操作系统,更加充分地利用操作系统的高级特性,获得高性能的IO操作。
一、 NIO基本概念
NIO在磁盘IO处理和文件处理上有很多新的特性来提高性能,本文不作详细的解释,而仅仅介绍NIO在处理网络IO方面的新特点,这些特点是理解Grizzly的最基本的概念。
1. 数据缓冲(Buffer)处理
数据缓冲(Buffer)是IO操作的基本元素。其实从本质上来说,无论是磁盘IO还是网络IO,应用程序所作的所有事情就是把数据放到相应的数据缓冲当中去(写操作),或者从相应的数据缓冲中提取数据(读操作)。至于数据缓冲中的数据和IO设备之间的交互,则是操作系统和硬件驱动程序所关心的事情了。因此,数据缓冲在IO操作中具有重要的作用,是操作系统与应用之间的IO桥梁。在NIO的包中,Buffer类是所有类的基础。 Buffer类当中定义数据缓冲的基本操作,包括put、get、reset、clear、flip、rewind等,这些基本操作是进行数据输入输出的手段。每一个基本的Java类型(boolean除外)都有相应的Buffer类,例如CharBuffer、IntBuffer、 DoubleBuffer、ShortBuffer、LongBuffer、FloatBuffer和ByteBuffer。我们所关心的是 ByteBuffer,因为操作系统与应用程序之间的数据通信最原始的类型就是Byte
数据缓冲的另外一个重要的特点是可以在一个数据缓冲上再建立一个或多个视图(View)缓冲。这个概念有些类似于数据库视图的概念:在数据库的物理表 (Table)结构之上可以建立多个视图。同样,在一个数据缓冲之上也可以建立多个逻辑的视图缓冲。视图缓冲的用处很多,例如可以将Byte类型的缓冲当作Int类型的视图,来进行类型转换。视图缓冲也可以将一个大的缓冲看成是很多小的缓冲视图。这对提高性能很有帮助,因为创建物理的数据缓冲(特别是直接的数据缓冲)是非常耗时的操作,而创建视图却非常快。在Grizzly中就有这方面的考虑。
2. 异步通道(Channel)
Channel(后文又称频道,译法仅暗示存在多通道可选)是NIO的另外一个比较重要的新特点。Channel并不是对原有Java类的扩充和完善,而是完全崭新的实现。通过Channel,Java应用程序能够更好地与操作系统的IO服务结合起来,充分地利用上文提到的ByteBuffer,完成高性能的IO操作。Channel的实现也不是纯Java的,而是和操作系统结合紧密的本地代码。
Channel的一个重要的特点是在网络套接字频道(SocketChannel)中,可以将其设置为异步非阻塞的方式。
非阻塞方式的频道使用:
通过SocketChannel.configureBlocking(false)就可以将网络套接字频道设置为异步非阻塞模式。一旦设置成非阻塞的方式,从Socket中读和写就再也不会阻塞。虽然非阻塞只是一个设置问题,但是对应用程序的结构和性能却产生了天翻地覆的变化。
3. 有条件的选择(Readiness Selection)
熟悉UNIX的程序员对POSIX的select()或poll()函数应该比较熟悉。在现在大多数流行的操作系统中,都支持有条件地选择已经准备好的IO通道,这就使得只需要一个线程就能同时有效地管理多个IO通道。在JDK 1.4以前,Java语言是不具备这个功能的。
NIO通过几个关键的类来实现这种有条件的选择的功能:
(1) Selector
Selector类维护了多个注册的Channel以及它们的状态。Channel需要向Selector注册,Selector负责维护和更新Channel的状态,以表明哪些Channel是准备好的。
(2) SelectableChannel
SelectableChannel是可以被Selector所管理的Channel。FileChannel不属于Selectable- Channel,而SocketChannel是属于这类的Channel。因此在NIO中,只有网络的IO操作才有可能被有条件地选择。
(3) SelectionKey
SelectionKey用于维护Selector和SelectableChannel之间的映射关系。当一个Channel向Selector注册之后,就会返回一个SelectionKey作为注册的凭证。SelectionKey中保存了两类状态值,一是这个Channel中哪些操作是被注册了的,二是有哪些操作是已经准备好的
二、 使用NIO来提高系统扩展性
NIO使用非阻塞的API,通过实现少量的线程就能服务于大量的并发用户的请求。并且通过操作系统都支持的POSIX标准的select方式,来获得系统准备就绪的资源。使用这些手段,NIO就能够充分利用每个活动的线程来服务于大量的请求,减少系统资源的浪费。通常来说,一个NIO的服务架构会采用以下的结构。
使用NIO的server编程框架:
上面的结构比起阻塞式的框架都复杂一些。具体说明如下:
通过ServerSocketChannel.open()获得一个Server的Channel对象。
通过Selector.open()来获得一个Selector对象。
从Server的Channel对象上可以获得一个Server的Socket,并让它在80端口监听。
通过ServerSocketChannel.configureBlocking(false)可以将当前的Channel配置成异步非阻塞的方式。如果没有这一步,那么Channel默认的方式跟传统的一样,是阻塞式的。
将当前的Channel注册到Selector对象中去,并告诉Selector当前的Channel关心的操作是OP_ACCEPT,也就是当有新的请求的时候,Selector负责更新此Channel的状态。
在循环当中调用selector.select(),如果当前没有任何新的请求过来,并且原来的连接也没有新的请求数据到达,这个方法会阻塞住,一直等到新的请求数据过来为止。
如果当前都请求的数据到达,那么selector.select()就会立刻退出,这时候可以从selector.selectedKeys()获得所有在当前selector注册过的并且有数据到达的这些Channel的信息(SelectionKey)。
遍历所有的这些SelectionKey来获得相关的信息。如果某个SelectionKey的操作是OP_ACCEPT,也就是isAcceptable,那么可以判定这是那个Server Channel,并且是有新的连接请求到达了。
当有新的请求来的时候,通过accept()方法可以获得新的channel服务于这个新来的请求。然后通过configureBlocking(false)可以将当前的Channel配置成异步非阻塞的方式。
接着将这个新的channel也注册到selector中,并告诉Selector当前的Channel关心的操作是OP_READ,也就是当前Channel有新的数据到达的时候,Selector负责更新此Channel的状态。
如果在循环当中发现某个SelectionKey的操作是OP_READ,也就是isReadable,那么可以判定这不是那个Server Channel,而是在循环内部注册的连接Channel,表明当前SelectionKey对应的这个Channel有数据到达了。
有数据到达之后的处理方式是下面要详细讨论的问题,在这里,我们简单地用一个方法readDataFromSocket(key)来表示,功能就是从这个Channel中读取数据。
从这个框架结构中可以看到,在一个线程中可以同时服务于多个连接,包括Server的监听服务。在同一个时刻,并不是所有的连接都会有数据到达,因此为每一个连接分配单独的线程没有必要。使用异步非阻塞方式,可以使用很少的线程,通过Select的方式来服务于多个连接请求,效率大大提高。
一、 NIO基本概念
NIO在磁盘IO处理和文件处理上有很多新的特性来提高性能,本文不作详细的解释,而仅仅介绍NIO在处理网络IO方面的新特点,这些特点是理解Grizzly的最基本的概念。
1. 数据缓冲(Buffer)处理
数据缓冲(Buffer)是IO操作的基本元素。其实从本质上来说,无论是磁盘IO还是网络IO,应用程序所作的所有事情就是把数据放到相应的数据缓冲当中去(写操作),或者从相应的数据缓冲中提取数据(读操作)。至于数据缓冲中的数据和IO设备之间的交互,则是操作系统和硬件驱动程序所关心的事情了。因此,数据缓冲在IO操作中具有重要的作用,是操作系统与应用之间的IO桥梁。在NIO的包中,Buffer类是所有类的基础。 Buffer类当中定义数据缓冲的基本操作,包括put、get、reset、clear、flip、rewind等,这些基本操作是进行数据输入输出的手段。每一个基本的Java类型(boolean除外)都有相应的Buffer类,例如CharBuffer、IntBuffer、 DoubleBuffer、ShortBuffer、LongBuffer、FloatBuffer和ByteBuffer。我们所关心的是 ByteBuffer,因为操作系统与应用程序之间的数据通信最原始的类型就是Byte
数据缓冲的另外一个重要的特点是可以在一个数据缓冲上再建立一个或多个视图(View)缓冲。这个概念有些类似于数据库视图的概念:在数据库的物理表 (Table)结构之上可以建立多个视图。同样,在一个数据缓冲之上也可以建立多个逻辑的视图缓冲。视图缓冲的用处很多,例如可以将Byte类型的缓冲当作Int类型的视图,来进行类型转换。视图缓冲也可以将一个大的缓冲看成是很多小的缓冲视图。这对提高性能很有帮助,因为创建物理的数据缓冲(特别是直接的数据缓冲)是非常耗时的操作,而创建视图却非常快。在Grizzly中就有这方面的考虑。
2. 异步通道(Channel)
Channel(后文又称频道,译法仅暗示存在多通道可选)是NIO的另外一个比较重要的新特点。Channel并不是对原有Java类的扩充和完善,而是完全崭新的实现。通过Channel,Java应用程序能够更好地与操作系统的IO服务结合起来,充分地利用上文提到的ByteBuffer,完成高性能的IO操作。Channel的实现也不是纯Java的,而是和操作系统结合紧密的本地代码。
Channel的一个重要的特点是在网络套接字频道(SocketChannel)中,可以将其设置为异步非阻塞的方式。
非阻塞方式的频道使用:
SocketChannel sc = SocketChannel.open(); sc.configureBlocking(false); // nonblocking ... if (!sc.isBlocking()) { doSomething(cs); }
通过SocketChannel.configureBlocking(false)就可以将网络套接字频道设置为异步非阻塞模式。一旦设置成非阻塞的方式,从Socket中读和写就再也不会阻塞。虽然非阻塞只是一个设置问题,但是对应用程序的结构和性能却产生了天翻地覆的变化。
3. 有条件的选择(Readiness Selection)
熟悉UNIX的程序员对POSIX的select()或poll()函数应该比较熟悉。在现在大多数流行的操作系统中,都支持有条件地选择已经准备好的IO通道,这就使得只需要一个线程就能同时有效地管理多个IO通道。在JDK 1.4以前,Java语言是不具备这个功能的。
NIO通过几个关键的类来实现这种有条件的选择的功能:
(1) Selector
Selector类维护了多个注册的Channel以及它们的状态。Channel需要向Selector注册,Selector负责维护和更新Channel的状态,以表明哪些Channel是准备好的。
(2) SelectableChannel
SelectableChannel是可以被Selector所管理的Channel。FileChannel不属于Selectable- Channel,而SocketChannel是属于这类的Channel。因此在NIO中,只有网络的IO操作才有可能被有条件地选择。
(3) SelectionKey
SelectionKey用于维护Selector和SelectableChannel之间的映射关系。当一个Channel向Selector注册之后,就会返回一个SelectionKey作为注册的凭证。SelectionKey中保存了两类状态值,一是这个Channel中哪些操作是被注册了的,二是有哪些操作是已经准备好的
二、 使用NIO来提高系统扩展性
NIO使用非阻塞的API,通过实现少量的线程就能服务于大量的并发用户的请求。并且通过操作系统都支持的POSIX标准的select方式,来获得系统准备就绪的资源。使用这些手段,NIO就能够充分利用每个活动的线程来服务于大量的请求,减少系统资源的浪费。通常来说,一个NIO的服务架构会采用以下的结构。
使用NIO的server编程框架:
public class Server { public static void main(String[] argv) throws Exception { ServerSocketChannel serverCh = ServerSocketChannel.open(); Selector selector = Selector.open(); ServerSocket serverSocket = serverCh.socket(); serverSocket.bind(new InetSocketAddress(80)); serverCh.configureBlocking(false); serverCh.register(selector,SelectionKey.OP_ACCEPT); while(true){ selector.select(); Iterator it = selector.selectedKeys().iterator(); while (it.hasNext()) { SelectionKey key = (SelectionKey)it.next(); if (key.isAcceptable()) { ServerSocketChannel server =(ServerSocketChannel)key.channel(); SocketChannel channel = server.accept(); channel.configureBlocking(false); channel.register(selector,SelectionKey.OP_READ); } if (key.isReadable()) { readDataFromSocket(key); } it.remove(); } } } }
上面的结构比起阻塞式的框架都复杂一些。具体说明如下:
通过ServerSocketChannel.open()获得一个Server的Channel对象。
通过Selector.open()来获得一个Selector对象。
从Server的Channel对象上可以获得一个Server的Socket,并让它在80端口监听。
通过ServerSocketChannel.configureBlocking(false)可以将当前的Channel配置成异步非阻塞的方式。如果没有这一步,那么Channel默认的方式跟传统的一样,是阻塞式的。
将当前的Channel注册到Selector对象中去,并告诉Selector当前的Channel关心的操作是OP_ACCEPT,也就是当有新的请求的时候,Selector负责更新此Channel的状态。
在循环当中调用selector.select(),如果当前没有任何新的请求过来,并且原来的连接也没有新的请求数据到达,这个方法会阻塞住,一直等到新的请求数据过来为止。
如果当前都请求的数据到达,那么selector.select()就会立刻退出,这时候可以从selector.selectedKeys()获得所有在当前selector注册过的并且有数据到达的这些Channel的信息(SelectionKey)。
遍历所有的这些SelectionKey来获得相关的信息。如果某个SelectionKey的操作是OP_ACCEPT,也就是isAcceptable,那么可以判定这是那个Server Channel,并且是有新的连接请求到达了。
当有新的请求来的时候,通过accept()方法可以获得新的channel服务于这个新来的请求。然后通过configureBlocking(false)可以将当前的Channel配置成异步非阻塞的方式。
接着将这个新的channel也注册到selector中,并告诉Selector当前的Channel关心的操作是OP_READ,也就是当前Channel有新的数据到达的时候,Selector负责更新此Channel的状态。
如果在循环当中发现某个SelectionKey的操作是OP_READ,也就是isReadable,那么可以判定这不是那个Server Channel,而是在循环内部注册的连接Channel,表明当前SelectionKey对应的这个Channel有数据到达了。
有数据到达之后的处理方式是下面要详细讨论的问题,在这里,我们简单地用一个方法readDataFromSocket(key)来表示,功能就是从这个Channel中读取数据。
从这个框架结构中可以看到,在一个线程中可以同时服务于多个连接,包括Server的监听服务。在同一个时刻,并不是所有的连接都会有数据到达,因此为每一个连接分配单独的线程没有必要。使用异步非阻塞方式,可以使用很少的线程,通过Select的方式来服务于多个连接请求,效率大大提高。
评论
2 楼
maysnow1979
2009-06-02
学习了, 不太懂.呵呵
1 楼
bachmozart
2009-04-06
个人觉得Java的IO服用模型封装的实在不怎么样,很多地方不好理解,本来操作系统简单的API和编程模型都是比较好理解的,被Java封装一层后反而很难看清楚类之间的关系
发表评论
-
supesite系统视频播放功能的分析及设置默认打开页面播放
2009-12-16 20:34 1846最近做的一个php项目需求中有视频这个频道,于是研究了一下su ... -
php之smarty技术中文文档
2009-12-07 19:46 1153最近因工作关系投身于了php语言.基于上都是基于supesit ... -
phpcms转supesite转换程序bug修正
2009-12-03 21:13 1941因客户前期使用的门户站是phpcms产品,现要升级到康盛的su ... -
Android通用布局对象
2009-09-02 14:49 3097FrameLayout FrameLayout is the ... -
java p2p技术
2009-07-27 10:45 5141英文原文地址: http://ww ... -
承接Java项目
2009-07-13 15:09 933本人从事软件开发4年,主要为供电局做远程集抄、营销系统、MIS ... -
WEB前端高性能优化之JavaScript优化细节
2009-06-11 14:07 772作为一名网站开发WEB前端工程师,对自己开发的网站项目应该尽可 ... -
Ext官方中文教程
2009-03-25 15:09 3624Extjs.com官方中文教程列表: so good~~! ... -
程序员的十层楼(菜鸟,大虾,牛人......)
2009-03-19 10:41 1079到底中国的程序员水平比西方程序员水平差,还是中国有许多优秀的程 ... -
欲为Java技术大牛所需的25个学习要点
2009-02-24 10:17 7541. 你需要精通面向对象 ... -
Java对象的生命周期分析
2009-01-17 15:20 2478从以下可心看出使 ... -
软件工程师不可不知的10个概念
2009-01-17 13:27 674出色的软件工程师善用设计模式,勤于代码重构,编写单元测试,并对 ... -
类设计的技巧
2008-12-31 10:09 894设计一个类要明确这个类所要完成的功能,类里的成员变量和 ... -
架构设计的几个心得
2008-12-24 09:57 849一,不要过设计:never o ... -
B/S前端优化
2008-12-03 12:02 8321. 合并脚本文件 包括 ... -
30个优秀源码网站
2008-11-28 14:25 1109从csdn上摘录下来,记录一下 1.51源码:http:// ... -
避免发生与安全有关的javascript错误
2008-11-28 11:40 941會產生 JavaScript錯誤: eval() 函數 s ... -
java socket/Serversocket编程详解(中/英文)
2008-10-23 15:09 2376socket /套接字 Sockets let y ... -
ext中文API文档及例子
2008-09-26 18:51 5285附件为一个chm文件,主要以ext文档为主,还附有一些ext包 ... -
Eclipse匆删恢复
2008-09-20 16:05 1270昨天coding时,不知道是脑袋抽筋还是咋滴了,一不小心把一个 ...
相关推荐
个人整理的pdf格式 关于本教程 新的输入/输出 (NIO) 库是在 JDK 1.4...除了彻底介绍与本教程有关的所有概念外,我还保持代码示例尽可能短小和简单。目的是让即使没有多少 Java 编程经验的读者也能容易地开始学习 NIO。
两个简单示例 495 第十五章 URLConnection 501 打开URLConnection 502 读取服务器的数据 503 读取首部 505 配置连接 514 配置客户端的请求HTTP首部 523 向服务器写入数据 525 内容处理器 530 Object方法 ...
两个简单示例 495 第十五章 URLConnection 501 打开URLConnection 502 读取服务器的数据 503 读取首部 505 配置连接 514 配置客户端的请求HTTP首部 523 向服务器写入数据 525 内容处理器 530 Object方法 ...
两个简单示例 495 第十五章 URLConnection 501 打开URLConnection 502 读取服务器的数据 503 读取首部 505 配置连接 514 配置客户端的请求HTTP首部 523 向服务器写入数据 525 内容处理器 530 Object方法 ...
两个简单示例 495 第十五章 URLConnection 501 打开URLConnection 502 读取服务器的数据 503 读取首部 505 配置连接 514 配置客户端的请求HTTP首部 523 向服务器写入数据 525 内容处理器 530 Object方法 ...
两个简单示例 495 第十五章 URLConnection 501 打开URLConnection 502 读取服务器的数据 503 读取首部 505 配置连接 514 配置客户端的请求HTTP首部 523 向服务器写入数据 525 内容处理器 530 Object方法 ...
两个简单示例 495 第十五章 URLConnection 501 打开URLConnection 502 读取服务器的数据 503 读取首部 505 配置连接 514 配置客户端的请求HTTP首部 523 向服务器写入数据 525 内容处理器 530 Object方法 ...
两个简单示例 495 第十五章 URLConnection 501 打开URLConnection 502 读取服务器的数据 503 读取首部 505 配置连接 514 配置客户端的请求HTTP首部 523 向服务器写入数据 525 内容处理器 530 Object方法 ...
两个简单示例 495 第十五章 URLConnection 501 打开URLConnection 502 读取服务器的数据 503 读取首部 505 配置连接 514 配置客户端的请求HTTP首部 523 向服务器写入数据 525 内容处理器 530 Object方法 ...
两个简单示例 495 第十五章 URLConnection 501 打开URLConnection 502 读取服务器的数据 503 读取首部 505 配置连接 514 配置客户端的请求HTTP首部 523 向服务器写入数据 525 内容处理器 530 Object方法 ...
两个简单示例 495 第十五章 URLConnection 501 打开URLConnection 502 读取服务器的数据 503 读取首部 505 配置连接 514 配置客户端的请求HTTP首部 523 向服务器写入数据 525 内容处理器 530 Object方法 ...
两个简单示例 495 第十五章 URLConnection 501 打开URLConnection 502 读取服务器的数据 503 读取首部 505 配置连接 514 配置客户端的请求HTTP首部 523 向服务器写入数据 525 内容处理器 530 Object方法 ...
两个简单示例 495 第十五章 URLConnection 501 打开URLConnection 502 读取服务器的数据 503 读取首部 505 配置连接 514 配置客户端的请求HTTP首部 523 向服务器写入数据 525 内容处理器 530 Object方法 ...
两个简单示例 495 第十五章 URLConnection 501 打开URLConnection 502 读取服务器的数据 503 读取首部 505 配置连接 514 配置客户端的请求HTTP首部 523 向服务器写入数据 525 内容处理器 530 Object方法 ...
两个简单示例 495 第十五章 URLConnection 501 打开URLConnection 502 读取服务器的数据 503 读取首部 505 配置连接 514 配置客户端的请求HTTP首部 523 向服务器写入数据 525 内容处理器 530 Object方法 ...