Java NIO类库Selector机制解析--转
??
版本以来,发布了全新的类库,简称,其不但引入了全新的高效的机制,同时,也引入了多路复用的异步模式。的包中主要包含了这样几种抽象数据类型:
类库书写相关网络程序的时候,发现了一些异常,异常的报错信息让他开始了对的进行了一些调查。当赵锟对我共享了的一些底层机制的猜想和调查时候,我们觉得这是一件很有意思的事情,于是在伙同赵锟进行过一系列的调查后,我俩发现了很多有趣的事情,于是导致了这篇文章的产生。这也是为什么本文的作者署名为我们两人的原因。
的开发人员,对于,这并不是我们的长处,这篇文章本质上出于对的的好奇,因为从表面上来看似乎做到了一些让我们这些出身的人比较惊奇的事情。
??
类库,超容易的跨平台,除了在性能上有些微辞,出身的程序员从来都不会觉得是一件很困难的事情。当然,对于长期习惯于使用操作系统(系统调用)的程序来说,面对中的比较“另类”地操作系统资源的方法可能会略感困惑,但万变不离其宗,只需要对面向对象的设计模式有一定的了解,用不了多长时间,的类库也能玩得随心所欲。
进行相关网络程序的的设计时,出身的人,首先想到的框架就是多路复用,想到多路复用,下马上就能让从想到系统调用。于是,在看到的中的类时必然会倍感亲切。稍加查阅一下手册以及相关例程,不一会儿,一个多路复用的框架便呈现出来,随手做个单元测试,没啥问题,一切和照旧。然后告诉兄弟们,框架搞定,以后咱们就在上开发及单元测试,完成后到运行环境上集成测试。心中并暗自念到,跨平台就好啊,开发活动都可以跨平台了。
上单元测试运行开始出现异常,看着运行异常出错的函数栈,异常居然由抛出,错误信息居然是。
居然报错误,凭什么?不应该啊?的时候又没有什么的连接,怎么会报这个错?
的程序当然会对操作系统的调用非常熟悉,虽然的虚拟机搞的什么系统调用都不见了,但的程序员必然要比程序敏感许多。
??
的老鸟从。果然,打开运行进程,发现有一些自己连接自己的的链接。于是另一个问题又出现了,
凭什么啊?为什么会有自己和自己的连接?我程序里没有自己连接自己啊,怎么可能会有这样的链接啊?而自己连接自己的端口号居然是些奇怪的端口。
在做怪?难道要创建一个自己连接自己的链接?写个程序看看:
import import import publicclass privatestaticfinalint5 publicstaticfinalvoid new
try forint0
//sels[i].close();
30000 catch thrownew
次,然后休息秒,以便我使用工具来查看进程。程序编译没有问题,运行起来,在中看到下面的对话框:(居然有个连接,从连接端口我们可以知道,互相连接,如:第一个连第二个,第二个又连第一个) 啊,先不说这是不是一件愚蠢的事。至少可以肯定的是,在消耗宝贵的系统资源方面,已经可以赶的上某些蠕虫病毒了。
的值改成试试,不一会你就会发现你的程序有这样的错误了:(在我的机器上大约运行到个左右)
Unable to establish loopback connection Unable to establish loopback connection (Unknown Source) (Unknown Source) No buffer space available (maximum connections reached?):?connect
??
,但这正好可以让我们来明白背着大家在干什么事。上面的那些“愚蠢连接”是在平台上,如果不出意外,下应该也差不多吧。
下跑了跑。使用命令,并没有看到自己和自己的连接。貌似在上使用了和不一样的机制?!
上不建自己和自己的连接的话,那么文件描述符和端口都会被省下来了,是不是也就是说我们调用个的话,应该不会出现异常了。
个左右,还不如)
Too many open files Too many open files (EPollSelectorImpl.java:49)
,于是我想到了使用命令来查看一下打开的文件。
文件,一共对,个(当然,管道从来都是成对的)。如下图所示。 在下不用连接,而是用管道。看来,这个管道也是自己给自己的。所以,我们可以得出下面的结论:
下,会自己和自己建立两条链接。不但消耗了两个连接和端口,同时也消耗了文件描述符。 下,会自己和自己建两条管道。同样消耗了两个系统的文件描述符。
下,的之所以选择连接,而不是,要么是因为性能的问题,要么是因为资源的问题。可能,下的管道的性能要慢于链接,也有可能是下的管道所消耗的资源会比链接多。这些实现的细节还有待于更为深层次的挖掘。
的在不同平台上的机制。 ??
的要设计成这个样子?如果说老的不能多路复用,如下图所示,要开多的线程去挨个侦听每一个文件描述符,如果这样做很费资源,且效率不高的话。那为什么在新的机制依然需要自己连接自己,而且,还是重复连接,消耗双倍的资源?
搜索引擎没有找到为什么。只看到多的人在报,但却没有任何解释。
和新的在网络编程方面的差别。看起来的确很好很强大。但似乎比起来说,的这种实现会有一些不必要的开销。 ??
的框架。当我们把框架的源码研读了一下后。发现在中有这么一个机制:
框架会创建一个对象的线程。 对象的线程的方法会从一个队列中拿出一堆,然后使用方法来侦听是否有数据可以读写。 最关键的是,在的时候,如果队列有新的加入,那么,会被唤醒,然后重新最新的集合。 要唤醒方法,只需要调用的方法。
程序员来说,一个阻塞在上的线程有以下三种方式可以被唤醒: ??写,或出现异常。 ??。 ??的信号。可由或发出。 要唤醒阻塞的,那么也只能通过这三种方法,其中:
第二种方法可以排除,因为一旦阻塞,应无法修改其时间。 而第三种看来只能在上实现,上没有这种信号通知的机制。
,在会建立一对自己和自己的的连接;在上会开一对(在下一般都是成对打开),估计我们能够猜得出来——那就是如果想要唤醒,只需要朝着自己的这个连接发点数据过去,于是,就可以唤醒阻塞在线程了。
??
下的命令,我们可以方便地证明这一点。参看下图。图中,请注意下面几点: ??字符串是为了做一个标记,而不至于迷失在大量的中。 ??阻塞的线程。 ??的正是方法的系统调用,而紧接着的就是的的返回。 的自己和自己建的那些连接或是,正是用来实现的和的功能的。
中的的和给阻塞在上的线程发信号的。但因为发信号这个东西并不是一个跨平台的标准(这个系统调用也不是所有都支持的),而是所有的所支持的,但又不支持,所以,用了连接来实现这个事。
,我一直在想,的防火墙的设置是不是会让的类似的程序执行异常呢?呵呵。如果不知道的有这样的机制,谁知道会有多少个程序为此引起的问题度过多少个不眠之夜,尤其是程序员。
??
的引出来的其它话题还有许多,比如关于的编译器又是如何,它是否会像的解释器如此做傻事?我在这里先卖一个关子,关于的编译器,我会在另外一篇文章中讲述,近期发布,敬请期待。
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |