小弟闲来无事, 封装了下nio,  在单线程开发中没遇到问题,  但是在引入了  java 自带的executor 来处理任务时, 碰到了个让小弟楞是蛋疼的问题, 先贴部分代码
  while (true) {
if (Status.RUNING.equals(status)) {
count = selector.select();
if (count != 0) {
selectionKeies = selector.selectedKeys().iterator();
while (selectionKeies.hasNext()) {
selectionKey = selectionKeies.next();
selectionKeies.remove();
attachment = selectionKey.attachment();
if (attachment instanceof Runnable) {
//XXX 多线程安全问题, 等待解决
                                                        //问题就在这 如,客户端连接当前服务,selector产生了一个Accept selectionKey, 当把这个任务分发给线程池去处理时,  出现了线程 快慢问题, 也就是 线程池还没处理, 当前线程继续了第2次count = selector.select();, 又产生一个Accept selectionKey,  分发给executor处理,这不是我想要的,两个任务处理一个连接,在非阻塞中会出现nullPoinException,   有时候甚至出现 10多次重复的SelectionKey,还麻烦做过nio的 朋友们帮帮忙
executor.execute((Runnable) attachment);
}
}
}
} else if (Status.STOP.equals(status)) {
//睡觉
bed.sleep();
changeStatus(Status.RUNING);
} else {
changeStatus(Status.SHUTDOWN);
break;
}
这段代码存在NioServerBootStart类中小弟项目直接在google svn 开源的。http://shenliuyang.googlecode.com/svn/trunk/ 同事 代码里也实现了数据传输协议哦(CatServerTest), 没有学过nio的可以看看, 改一句代码就可以运行的,在NioServerBootStart有提示

解决方案 »

  1.   

    在你的这句代码executor.execute((Runnable) attachment);首先使用
    ServerSocketChannel server = (ServerSocketChannel) key.channel();
    SocketChannel channel = server.accept();
    去接收这个请求再往下处理,即已经接收这个请求了,这样,就不会产生第二次accept请求了
      

  2.   

    首先谢谢你给出的建议
    这样是肯定不行的哦, 所有的请求肯定是要任务线程去处理的, 不光接收,客户端还要发数据呢,  我现在想到了一个办法, 那就是拿到SelectionKey的时候把他注册的时间给remove掉, 在把任务分给任务池处理, 在在各个任务中处理完数据后, 在加上相应的事件, 这样估计就没问题了,不 过还没试过,  好累、、、公司搞活动刚回来, 看到你们的回答,  太感谢你们啦
      

  3.   

    你的代码中已经 selectionKeys.remove() 了,所以下一次再得到的 Accept Selection Key 和上一次不是同一个了,我觉得并不是什么东西都需要使用异步处理的,像 Accept 这种事件我觉得可以阻塞处理,它相应的逻辑上的任务也就是初始化,这个过程不应该做太多的事情,公共的初始化部分可以在 socket 开始处理请求之前就完成整个的环境初始化,Accept 的初始化仅应用于这个连接本身相关的东西。之后的 其它事件如 Read 事件,之类的可以把“消息”放入请求队列中,让线程池去请求队列中取消息进行处理,处理的结果投递到“响应”队列中,当某个 socket 有 write 事件时,响应队列中也有回复消息时就趁着 write 事件出现的时机发送出去。这里面传输层和消息处理的应用层是分开的,它们之间通过消息队列松耦合。我之前的买股票的消息交换功能的传输层就是这么设计的,请求和响应队列都不需要阻塞,只需要同步就可以了,一般一条消息在一个事件中发送出去没多大问题的,网络不至于丢包到那情况吧?就算是网络状态差,发不出去的话,收到的请求自然也会少了,不用太担心堆集成灾的问题。
      

  4.   

    朋友, remove SelectionKey后如果不及时处理, 下一次select还是会出来的, 再者如果像你所说  不给ServerSocketChannel 非阻塞, 那样的话就违反selector的初衷了, 不光accept事件会发生多次, read事件也会发送多次,  都是重复的哦。 我用一个客户端连接测试的。
      

  5.   

    当 accept 事件发生时,立即调用 ServerSocketChannel.accept() 而不必去异步处理这个事件,因为 accept 事件发生时,去调用 ServerSocketChannel.accept 肯定会立即成功不会发生阻塞。每建立一个连接就相应地有少量初始化工作来为这个连接设置参数,这个过程没必要异步处理,我们应该让一个连接建立时的初始化工作尽可能地少来提交响应速度;至于后面的消息处理过程,我们也应该是收到消息时尽量当成 byte[] 来保存到“请求队列”中而不需要做太多地处理,处理过多就会导致响应慢,在这里,消息被放到请求队列的过程也是同步的,不需要异步。异步的处理应用在消息处理本身而不收到消息的过程。我们用异步处理也是尽可能地合理平衡资源利用率,后面都跟不上的话那总体效率也还是没有提高上来。