2012-06-20 37 views
2

我一直在尝试将NIO与多线程读取处理进行配对,以便构建一个可伸缩的服务器。我不能使用Netty或MINA等任何框架,因为有几个低级别的客户端 - 服务器协议细节,这些细节在实现时都会花费太多精力。我只是检查我的代码,我意识到有这个片段潜在的竞争条件:用于异步服务器的Java NIO和多线程

//executes in selector thread 
public void runSelector() { 
    //... 
    Set<SelectionKey> keys = selector.selectedKeys(); 
    for (Iterator<SelectionKey> keyIter = keys.iterator(); keyIter.hasNext();) { 
     final SelectionKey key = keyIter.next(); 
     keyIter.remove(); 
     if (key.isValid() && key.isReadable()) { //point A 
      //maybe some other short calculations 
      ((SocketChannel) key.channel()).read(buffer); //point B 
      workerThreadPool.submit(new Runnable() { public void run() { processRead(key, buffer); } }); 
     } 
    } 
    //... 
} 

//executes in a worker thread 
private void processRead(SelectionKey key, ByteBuffer buf) { 
    //... somewhere 
    key.cancel(); 
    //... 
} 

这是极不可能的事件,但它是完全有可能的是在一个工作线程,我打电话key.cancel( )而选择器线程位于我在runSelector()方法中评论的两个点之间。请记住,这可以部署在高并发机器上,并且运行选择器线程的CPU内核可能会陷入困境。 key.cancel()是否可以在选择器线程中的key.isReadable()和channel.read()之间的工作线程中调用并导致CancelledKeyException?我是否应该有某种线程安全的集合来存储所有要取消的键,以便runSelector()可以在迭代结束时取消所有这些键? Netty或MINA等更专业的项目如何处理这种情况?

回答

1

锁定资源,

synchronized(key) { 
    ((SocketChannel) key.channel()).read(buffer); //point B 
} 

如果着眼于对并发this甲骨文教程奇怪的样子。

+0

哇,我不敢相信我没有想到同步。我想其中一个解决方案是将key.cancel()与synchronized同步并将key.isValid()&& key.isReadable()与同步的块封装在一起。非常感谢! –