0 0

NIO SelectionKey.cancel只对下一次select()生效?30

貌似SelectionKey.cancel只对下一次的Selector.cancel()有效,代码如下,只贴了简要的:

nKeys = selector.select();
ExecutorService exec = Executors.newFixedThreadPool(10);
if (nKeys > 0)
{
   Set<SelectionKey> selectedKeys = selector.selectedKeys();
   Iterator<SelectionKey> it = selectedKeys.iterator();
   while (it.hasNext())
   {
       SelectionKey selectionKey = (SelectionKey) it.next();
       it.remove();
       if (selectionKey.isAcceptable())
       {
          。。。  //省略了accept()通道的注册代码
       }
       else if(selectionKey.isReadable())
       {
           exec.execute(new ProcessThread(selectionKey));   //如果客户端数据没有处理完整,将会重新注册该key
           selectionKey.cancel();
       }
   }
}

客户端只连了一次,在ProcessThread(selectionKey).run的处理过程中,select()还有两次成功返回,第一次返回时nKeys=0,第二次nKey=1。如果使用单步调试,也就是在exec.execute(new ProcessThread(selectionKey))和下一次select()之间有足够的时间处理通道的数据,则select()将阻塞。
不太明白为什么selectionKey.cancel()不起作用。

问题补充:
beneo 写道
引用
貌似SelectionKey.cancel只对下一次的Selector.cancel()有效
是对下一次select()有效

简单的说
cancel()方法是永久的注销SelectionKey.OPxxxx,并将其放入selector的canceled set中。在下一次调用select()方法的时候,这些键会从该选择器的所有键集中移除,它关联的信道也不在监听了(除非它又重新注册)

此外,你用 exec.execute 这种含有异步的单步调试不一定准确


你说“它关联的信道也不在监听了(除非它又重新注册)”,但为什么我在一次select()返回0后,又多一次select()返回1,在这个时间里面我没有重新注册这个Key。我的感觉是“只对下一次select有效”,你说的是“对下一次select()有效”。

问一下:我的监听端口用一个线程,但处理具体的读操作想用线程池。这样写代码就会出现多个线程处理同一个通道的数据,客户端的数据可能要多次读事件才能完整读取。所以会出问题,我必须保证,同一个时间只有一个线程在处理某个通道。

问题补充:在我调用了selectionKey.cancel后,ProcessThread处理完该通道上的数据之前,selectionKey.isReadable()还是返回true。
我想要知道的是这个:我怎么保证我将selectionKey提交给ProcessThread后,重新注册之前,不要再响应该通道的readable事件。

ExecutorService exec = Executors.newFixedThreadPool(10); 这个代码是放在while外面的,是为了让问题相关的东西都贴出来,忘记了这个顺序,谢谢提醒。

问题补充:
beneo 写道
selectionKey.cancel();
放在
exec.execute(new ProcessThread(selectionKey)); 
前面


还是一样,

问题补充:
beneo 写道
你看看你多线程里面有没有
key.interestOps(SelectionKey.OP_READ)这样类似的操作,你先注释掉。。


我仔细找了一下,我没有再向这个KEY注册什么东西。会不会是nio有什么bug?和jdk版本有关吗?
2010年12月30日 21:54

7个答案 按时间排序 按投票排序

0 0

采纳的答案

我们只是创建了一个selectedKey

第一次注册accepte

在select()后,返回 1,因为含有一个selectedKey

在handle accepte 里面,key.insertOtps(read)

在selcte()后,返回1,因为只有一个selectedKey

这个时候进入isReadable
在这个时候cancel()

那么第三次select()后,就是0了。。因为没有selectedKey了

2010年12月31日 00:11
0 0

不好说,不过我觉得不是一个bug,我也不知道你到底注册了多少个SelectedKey

我用的是最经典的nio例子,打印的结果就是这样。。。

        while (true) {

            if (selector.select(TIMEOUT) == 0) {
                System.out.println(".");
                continue;
            }
            Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
            while (keyIter.hasNext()) {
                int x = 0;
                SelectionKey key = keyIter.next();

                if (key.isAcceptable()) {
                    System.out.println(" ... is acceptable ...");
                    protocol.handleAccept(key);

                } else if (key.isReadable()) {
                    System.out.println(" ... is readable ...");
                    protocol.handleRead(key);
                    key.cancel();

                    System.out.println(" ... cancel ... ");
                } else if (key.isWritable() && key.isValid()) {
                    System.out.println(" --- isWritable");
                    protocol.handleWrite(key);
                }
                // 由于 select() 操作只是向 Selector 所关联的键集合中添加元素
                // 因此,如果不移除每个处理过的键,
                // 它就会在下次调用 select() 方法时仍然保留在集合中
                // 而且可能会有无用的操作来调用它。
                keyIter.remove();
            }

        }



.
.
.
.
 ... is acceptable ...
 ... is readable ...
 ... cancel ... 
.
.
.
.


2010年12月31日 00:05
0 0

你看看你多线程里面有没有
key.interestOps(SelectionKey.OP_READ)这样类似的操作,你先注释掉。。

2010年12月30日 23:55
0 0

selectionKey.cancel();
放在
exec.execute(new ProcessThread(selectionKey)); 
前面

2010年12月30日 23:48
0 0

引用
你说“它关联的信道也不在监听了(除非它又重新注册)”,但为什么我在一次select()返回0后,又多一次select()返回1,在这个时间里面我没有重新注册这个Key



返回2个1不奇怪,你可能把select()的返回与SelectedKey.op值弄混了。

select()返回的是一组键

你可以在isReadable下面打印东西看看,看有没有问题。

此外,你的多线程还是有问题

如果你贴出来的代码在整个的while循环里,希望你将
ExecutorService exec = Executors.newFixedThreadPool(10); 
放在class variable,不是放在方法里面,否则有内存泄露




引用
我的感觉是“只对下一次select有效”,你说的是“对下一次select()有效”。


在每次选择操作期间,都可以将键添加到选择器的已选择键集以及从中将其移除,并且可以从其键集和已取消键集中将其移除。选择是由 select()、select(long) 和 selectNow() 方法执行的,执行涉及三个步骤:

   1.将已取消键集中的每个键从所有键集中移除(如果该键是键集的成员),并注销其通道。此步骤使已取消键集成为空集。
   2.在开始进行选择操作时,应查询基础操作系统来更新每个剩余通道的准备就绪信息,以执行由其键的相关集合所标识的任意操作。对于已为至少一个这样的操作准备就绪的通道,执行以下两种操作之一:
         a.如果该通道的键尚未在已选择键集中,则将其添加到该集合中,并修改其准备就绪操作集,以准确地标识那些通道现在已报告为之准备就绪的操作。丢弃准备就绪操作集中以前记录的所有准备就绪信息。
         b. 如果该通道的键已经在已选择键集中,则修改其准备就绪操作集,以准确地标识所有通道已报告为之准备就绪的新操作。保留准备就绪操作集以前记录的所有准备就绪信息;换句话说,基础系统所返回的准备就绪操作集是和该键的当前准备就绪操作集按位分开 (bitwise-disjoined) 的。
   3.如果在此步骤开始时键集中的所有键都有空的相关集合,则不会更新已选择键集和任意键的准备就绪操作集。
   如果在步骤2的执行过程中要将任意键添加到已取消键集中,则处理过程如步骤1。

2010年12月30日 22:56
0 0

是的。 这是java的一个设计, 就是已经有的事件, 一定要处理掉的。

2010年12月30日 22:02
0 0

引用
貌似SelectionKey.cancel只对下一次的Selector.cancel()有效
是对下一次select()有效

简单的说
cancel()方法是永久的注销SelectionKey.OPxxxx,并将其放入selector的canceled set中。在下一次调用select()方法的时候,这些键会从该选择器的所有键集中移除,它关联的信道也不在监听了(除非它又重新注册)

此外,你用 exec.execute 这种含有异步的单步调试不一定准确

2010年12月30日 22:02

相关推荐

    java NIO.zip

    Java NIO,全称为Non-Blocking Input/Output(非阻塞输入/输出),是Java标准库提供的一种替代传统的I/O模型的新技术。自Java 1.4版本引入NIO后,它为Java开发者提供了更高效的数据传输方式,尤其是在处理大量并发...

    xnio-nio-3.8.0.Final-API文档-中文版.zip

    赠送jar包:xnio-nio-3.8.0.Final.jar; 赠送原API文档:xnio-nio-3.8.0.Final-javadoc.jar; 赠送源代码:xnio-nio-3.8.0.Final-sources.jar; 赠送Maven依赖信息文件:xnio-nio-3.8.0.Final.pom; 包含翻译后的API...

    NIO 入门.chm,NIO 入门.chm

    **NIO(New Input/Output)是Java编程语言中用于替代标准I/O(BIO,Blocking I/O)的一组API,它提供了非阻塞式的I/O操作方式,极大地提升了Java在处理I/O密集型应用时的性能。NIO在Java 1.4版本中被引入,之后在...

    NIO流程.txt

    ### NIO流程详解 #### 一、概述 NIO(Non-blocking Input/Output,...相比于传统的阻塞式I/O,NIO能够更有效地利用系统资源,尤其适合于高并发场景下的应用开发。掌握NIO的基本流程对于理解和应用这种技术至关重要。

    NioServer.java

    NioServer.java

    niodemo.zip

    而NIO则通过选择器实现了一对多的模型,一个线程可以处理多个连接,提高了系统资源利用率,尤其适合高并发场景。 7. **NIO的应用场景**:Java NIO广泛应用于网络编程,如服务器端的高性能Web应用、聊天服务器、游戏...

    Java IO, NIO and NIO.2

    Java IO、NIO以及NIO.2是Java中用于处理输入/输出操作的三种主要机制。本书《Java IO, NIO and NIO.2》旨在深入浅出地介绍这些机制,同时书中内容均为英文。接下来将详细介绍这些知识点。 **Java IO** Java IO是...

    java NIO详细教程

    ### Java NIO 详细教程知识点解析 #### 一、Java NIO 概述 ...以上就是Java NIO详细教程的知识点解析,希望对你有所帮助。Java NIO是一种强大的技术,它能够显著提高应用程序在网络IO方面的性能和扩展性。

    NIO-实践-多线程实例

    NIO用于高性能Socket编程由来已久,网络也有较为丰富的原理和源代码。我这里主要介绍几点经验总结: ...本文粘贴多线程在NIO环境下的基本运用示例代码,同时演示了一个线程如何对多个连接进行读写的操作。

    Java IO, NIO and NIO.2(Apress,2015)

    Java I/O, NIO, and NIO.2 is a power-packed book that accelerates your mastery of Java's various I/O APIs. In this book, you'll learn about classic I/O APIs (File, RandomAccessFile, the stream classes ...

    JAVA NIO ChannelDemo.zip

    JAVA NIO ChannelDemo.zip

    java nio.pdf

    java nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava ...

    nio.rar_FastCopyFile.java_NIO_UseFloatBuffer.java_java nio_文件锁

    在多线程或者网络环境下,有时我们需要确保文件在同一时刻只被一个线程或进程访问,这就需要用到文件锁。Java NIO提供了FileLock接口来实现这一功能。你可以通过FileChannel的lock()和tryLock()方法获取文件锁。锁...

    Tutorialspoint Java NIO 教程.epub

    Tutorialspoint Java NIO 教程.epub

    NIO笔记.doc

    Java NIO(New Input/Output)是Java标准库中提供的一种替代传统IO(基于流的I/O)模型的机制,其设计目标是为了提高程序在处理I/O操作时的性能。NIO的核心概念主要包括通道(Channel)、缓冲区(Buffer)和选择器...

    Java IO, NIO and NIO.2 原版pdf by Friesen

    New I/O (NIO), and NIO.2 categories. You learn what each category offers in terms of its capabilities, and you also learn about concepts such as paths and Direct Memory Access. Chapters 2 through 5 ...

    xnio-nio-3.8.0.Final-API文档-中英对照版.zip

    赠送jar包:xnio-nio-3.8.0.Final.jar; 赠送原API文档:xnio-nio-3.8.0.Final-javadoc.jar; 赠送源代码:xnio-nio-3.8.0.Final-sources.jar; 赠送Maven依赖信息文件:xnio-nio-3.8.0.Final.pom; 包含翻译后的API...

Global site tag (gtag.js) - Google Analytics