分裂的咖啡豆

导航

 

 

CSDN的内容直接粘贴过来格式总是弄不好,我也懒得弄了.下面是传送门

http://blog.csdn.net/haoel/article/details/2224055

原文分上下两篇,我在这里做一下总结,也就囫囵吞枣了吧..

我们在学习NIO的时候经常回想写BIO一样写成多线程的.在每个线程里都写了Selector.select()可是连接一多就会报异常

Exception in thread "main" java.lang.RuntimeException: java.io.IOException: Unable to establish loopback connection
        at Test.main(Test.java:18)
Caused by: java.io.IOException: Unable to establish loopback connection
        at sun.nio.ch.PipeImpl$Initializer.run(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at sun.nio.ch.PipeImpl.<init>(Unknown Source)
        at sun.nio.ch.SelectorProviderImpl.openPipe(Unknown Source)
        at java.nio.channels.Pipe.open(Unknown Source)
        at sun.nio.ch.WindowsSelectorImpl.<init>(Unknown Source)
        at sun.nio.ch.WindowsSelectorProvider.openSelector(Unknown Source)
        at java.nio.channels.Selector.open(Unknown Source)
        at Test.main(Test.java:15)
Caused by: java.net.SocketException: No buffer space available (maximum connections reached?): connect
        at sun.nio.ch.Net.connect(Native Method)
        at sun.nio.ch.SocketChannelImpl.connect(Unknown Source)
        at java.nio.channels.SocketChannel.open(Unknown Source)
        ... 9 more

原因是每个Selector都会打开一个loopback接连(就是自己连接到自己).在win上是TCP,在linux是pipe.loopback资源有限
,打开的Selector一多自然就报错了.

如果要追述具体原因,这和Sun实现Selector的wakeup方法有关.JDK API里面这么写着的:

public abstract Selector wakeup()使尚未返回的第一个选择操作立即返回。 
如果另一个线程目前正阻塞在 select() 或 select(long) 方法的调用中,则该调用将立即返回。如果当前未进行选择操作,那么在没有同时调用 selectNow() 方法的情况下,对上述方法的下一次调用将立即返回。在任一情况下,该调用返回的值可能是非零的。如果未同时再次调用此方法,则照常阻塞 select() 或 select(long) 方法的后续调用。 

在两个连续的选择操作之间多次调用此方法与只调用一次的效果相同。 


返回:
此选择器


当我们调用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上的线程了。

原因弄清楚了之后,就该要吸取教训了.在写NIO代码的时候要注意 Selector的实例不宜过多.保持有效的几个就可以了.具体多少个可以参考mina的框架的处理.

 

 

 

 

posted on 2012-07-01 15:22  分裂的咖啡豆  阅读(798)  评论(0)    收藏  举报