陈皓专栏 【空谷幽兰,心如皓月】

芝兰生于深谷,不以无人而不芳;君子修道立德,不为困穷而改节。

陈皓ID:haoel
[修改头像]
429723次访问,排名99好友11人,关注者23
暂无
haoel的文章
原创 71 篇
翻译 0 篇
转载 0 篇
评论 998 篇
陈皓的公告
Email & MSN
haoel@hotmail.com
最近评论
zyj:支持一下
mini storage:事在人為嘛. 不過, 大環境實在變得太快. 今天的標準可以是明日黃花!
mini storage:事在人為嘛. 不過, 大環境實在變得太快. 今天的標準可以是明日黃花!
Untitled:首先看
"就是服务很繁忙的时候,要发起一个loopback可能比较困难,仅此而已~"

会用selector的情况一般都是server (不是?), 花费一个‘本地’连接可以提高性能的话是不容置疑会考虑的。(如果是servlet server, 甚至还会有precompile jsp 的选项). 由于这个连接是实现里面的,所以不能直接提供选项在API里 (如果……
littlehedgehog:Windows, on the other hand, was designed to please accountants.
看得出原书的作者很喜欢以自己技术人员自居,不过计算机是为全人类服务的!而不是专门为他这样自以为是的技术人员!一个对计算机几乎不了解只想点点鼠标就能完成工作的会计人员都能使用计算机,那么计算机的价值,程序的价值才真正的彻底的发挥出来。Windows能做到,所……
订阅我的博客
XML聚合  FeedSky
订阅到鲜果
订阅到Google
订阅到抓虾
订阅到BlogLines
订阅到Yahoo
订阅到GouGou
订阅到飞鸽
订阅到Rojo
订阅到newsgator
订阅到netvibes
文章分类
收藏
    相册
    我的BLOG
    耗子小筑(非技术)(RSS)
    陈皓专栏(技 术)(RSS)
    存档

    原创 Java NIO类库Selector机制解析(下)

    新一篇: 用GDB 调试Java程序

     

    Java NIO类库Selector机制解析(下)

     

    赵锟   陈皓

    http://blog.csdn.net/haoel

     

    <<<<点此查看本文上篇

    五、  迷惑不解 : 为什么要自己消耗资源?

     

    令人不解的是为什么我们的JavaNew I/O要设计成这个样子?如果说老的I/O不能多路复用,如下图所示,要开N多的线程去挨个侦听每一个Channel (文件描述符) ,如果这样做很费资源,且效率不高的话。那为什么在新的I/O机制依然需要自己连接自己,而且,还是重复连接,消耗双倍的资源?

     

    通过WEB搜索引擎没有找到为什么。只看到N多的人在报BUG,但SUN却没有任何解释。

     

    下面一个图展示了,老的IO和新IO的在网络编程方面的差别。看起来NIO的确很好很强大。但似乎比起C/C++来说,Java的这种实现会有一些不必要的开销。

     

     

     

    六、  它山之石 : ApacheMina框架了解Selector

     

    上面的调查没过多长时间,正好同学赵锟的一个同事也在开发网络程序,这位仁兄使用了ApacheMina框架。当我们把Mina框架的源码研读了一下后。发现在Mina中有这么一个机制:

     

    1)Mina框架会创建一个Work对象的线程。

    2)Work对象的线程的run()方法会从一个队列中拿出一堆Channel,然后使用Selector.select()方法来侦听是否有数据可以读/写。

    3)最关键的是,在select的时候,如果队列有新的Channel加入,那么,Selector.select()会被唤醒,然后重新select最新的Channel集合。

    4)要唤醒select方法,只需要调用Selectorwakeup()方法。

     

    对于熟悉于系统调用的C/C++程序员来说,一个阻塞在select上的线程有以下三种方式可以被唤醒:

    1)  有数据可读/写,或出现异常。

    2)  阻塞时间到,即time out

    3)  收到一个non-block的信号。可由killpthread_kill发出。

    所以,Selector.wakeup()要唤醒阻塞的select,那么也只能通过这三种方法,其中:

     

    1)第二种方法可以排除,因为select一旦阻塞,应无法修改其time out时间。

    2)而第三种看来只能在Linux上实现,Windows上没有这种信号通知的机制。

     

    所以,看来只有第一种方法了。再回想到为什么每个Selector.open(),在Windows会建立一对自己和自己的loopbackTCP连接;在Linux上会开一对pipepipeLinux下一般都是成对打开),估计我们能够猜得出来——那就是如果想要唤醒select,只需要朝着自己的这个loopback连接发点数据过去,于是,就可以唤醒阻塞在select上的线程了。

     

    七、  真相大白 : 可爱的Java你太不容易了

     

    使用Linux下的strace命令,我们可以方便地证明这一点。参看下图。图中,请注意下面几点:

    1)  26654是主线程,之前我输出notify the select字符串是为了做一个标记,而不至于迷失在大量的strace log中。

    2)  26662是侦听线程,也就是select阻塞的线程。

    3)  图中选中的两行。26654write正是wakeup()方法的系统调用,而紧接着的就是26662epoll_wait的返回。

     

     

    从上图可见,这和我们之前的猜想正好一样。可见,JDKSelector自己和自己建的那些TCP连接或是pipe,正是用来实现Selectornotifywakeup的功能的。

     

    这两个方法完全是来模仿Linux中的的killpthread_kill给阻塞在select上的线程发信号的。但因为发信号这个东西并不是一个跨平台的标准(pthread_kill这个系统调用也不是所有Unix/Linux都支持的),而pipe是所有的Unix/Linux所支持的,但Windows又不支持,所以,Windows用了TCP连接来实现这个事。

     

    关于Windows,我一直在想,Windows的防火墙的设置是不是会让Java的类似的程序执行异常呢?呵呵。如果不知道JavaSDK有这样的机制,谁知道会有多少个程序为此引起的问题度过多少个不眠之夜,尤其是Java程序员。

     

    八、  后记

     

    文章到这里是可以结束了,但关于Java NIOSelector引出来的其它话题还有许多,比如关于GNU Java编译器又是如何,它是否会像SunJava解释器如此做傻事?我在这里先卖一个关子,关于GNUJava编译器,我会在另外一篇文章中讲述,近期发布,敬请期待。

     

    关于本文中所使用的实验平台如下:

    ·        WindowsWindows XP + SP2, Sun J2SE (build 1.7.0-ea-b23)

    ·        LinuxUbuntu 7.10 + Linux Kernel 2.6.22-14-generic, J2SE (build 1.6.0_03-b05)

     

    本文主要的调查工作由我的大学同学赵锟完成,我帮其验证调查成果及猜想。在此也向大家介绍我的大学同学赵锟,他也是一个技术高手,在软件开发方面,特别是Unix/Linux C/C++方面有着相当的功底,相信自此以后,会有很多文章会由我和他一同发布。

     

    本篇文章由我成文。但其全部著作权和版权归赵锟和我共同所有。我们欢迎大家转载,但希望保持整篇文章的完整性,并请勿用于任何商业用途。谢谢。

     

    如果有任何问题,欢迎使用MSN和邮件和我联系:haoel@hotmail.com

     

     

    <<<<点此查看本文上篇

     

     

    (转载时请注明作者和出处。未经许可,请勿用于商业用途)

     

    更多文章请访问我的Blog: http://blog.csdn.net/haoel

    发表于 @ 2008年03月27日 21:50:00|评论(loading...)|编辑

    旧一篇: Java NIO类库Selector机制解析(上)

    评论

    #ship 发表于2008-03-28 10:22:55  IP: 220.165.246.*
    嘿嘿,很好很好,期待下篇。
    #路过 发表于2008-03-31 10:34:53  IP: 222.129.46.*
    JDK如此实现java异步IO也没什么大不了的,跨平台就要封装一个统一的界面,至于底层实现肯定是要考虑多平台的兼容问题,Windows实现倒是可以不用自连Socket,而是使用WaitForMultipleObjects,详细情况见C++网络库、开发框架ACE,但是这个win32函数有windows自身跨平台问题;另外,网络程序根本不用打开太多Selector,如果打开太多Selector,应该说是程序设计的结构有可以调整、完善的地方。
    2008-04-11 14:12:01作者回复
    这个问题不是没什么大不了的问题。用N多的Selector不是设计而是实验,为的是更容易地揭露这个实现细节。另外,我在文中说过,如果你不知道JVM有这个细节,那怕你有很NB的设计,只用一个Selector,但也可能你在部署你的程序时,Windows的防火墙会出来让你的程序直接异常退出,而作为一个纯Java的程序员来说,这种事情估计对他来说是个恶梦。
    #bawojintianhr 发表于2008-03-31 14:52:37  IP: 60.176.193.*
    拜读了楼主的文章,确实不错,有收获,我最近也在研究NIO,但是很多地方都不是很懂,比如说,它是怎么实现多路复用的,为什么能这么节省资源呢。。
    2008-04-11 14:00:06作者回复
    你应该先去研究一下《Unix网络编程》
    #bobo 发表于2008-03-31 16:29:27  IP: 220.160.173.*
    其实你可以看一下Grizzly,另外一个很不错的nio框架,性能什么的比mina高多了,很不一样的一个实现..
    2008-04-11 13:59:08作者回复
    这个事情和Mina没有关系啊!无论你用什么样的框架,只要在JVM上运行,就是这个结果。
    #Ric 发表于2008-03-31 18:49:32  IP: 124.77.133.*
    同意"路过"的观点,Selector作为一个枢纽中心目的就是为高效复用提供基础,开过多Selector基本说明设计或使用上存在问题
    #liigo 发表于2008-04-11 13:21:25  IP: 60.20.2.*
    看来JAVA程序员和C++程序员,思考问题的角度确实是不太一样的。能想到一块去,谈何容易?
    #liigo 发表于2008-04-11 13:32:05  IP: 60.20.2.*
    这篇文章不应该结束啊,您只是“证实了”SUN的NIO实现中“存在自己连自己”这样一个事实,并没有解释SUN为什么这么做,也没有解释这么做有什么优势和劣势,似乎与题目“Java NIO类库Selector机制解析”不符。另:综合来看,NIO的效率如何?其实现是否明显影响了其效率?(上面有网友提到,“Selector作为一个枢纽中心目的就是为高效复用提供基础,开过多Selector基本说明设计或使用上存在问题”。)
    2008-04-11 14:02:03作者回复
    你问的这些问题,我都在我的文章里说到过了。1)为什么SUN这么做的理由我在文章中提到过了(因为需要跨平台)。2)这篇文章只讲述实现机制,和效率无关。Java的执行效率还是不说为好。3)为什么要开过多的Selector,文中已经说过,这是为了实验的目的。为的是更容易地揭露JVM的实现机制。
    #ciml 发表于2008-04-14 19:01:10  IP: 118.112.47.*
    谢谢,对我很有帮助。我正在困惑.net中socket类的静态方法Select,msdn中说它的超时参数设置为-1表示无限等待,但实际结果确实立刻返回,我不知道为什么。
    而我也正在迷惑为什么没有类似的wakeup来唤醒select,从你的分析中可以看出windows下没有这种机制,.net的实现当然就无须提供这样的方法,而java要跨平台,不得不采用自己连自己,发一些数据过去来实现wakeup。
    呵呵,要实现跨平台真的不容易啊!
    #dennis 发表于2008-04-22 18:00:00  IP: 116.23.108.*
    有unix编程经验,来搞java就是不一样啊,作者还是评价下效率问题,让我这个java程序员理解一二。
    #kilik 发表于2008-05-12 12:17:53  IP: 202.96.19.*
    我认为选择pipe方式来wakeup阻塞的selector,而不是信号的方式,应该是避免和通常的信号相混淆,而不是出于跨平台的考虑。 另外,在ubuntu里可以下载openjdk,可以看到selector的实现代码,看过之后对它的实现机理应该有一个更深刻的认识。
    发表评论  


    登录
    Csdn Blog version 3.1a
    Copyright © 陈皓