Selector:
public abstract class Selector
extends Object
SelectableChannel
对象的多路复用器。
可通过调用此类的 open
方法创建选择器,该方法将使用系统的默认
选择器提供者
创建新的选择器。也可通过调用自定义选择器提供者的
openSelector
方法来创建选择器。通过选择器的
close
方法关闭选择器之前,它一直保持打开状态。
通过
SelectionKey
对象来表示可选择通道到选择器的注册。选择器维护了三种选择键集:
-
键集 包含的键表示当前通道到此选择器的注册。此集合由
keys
方法返回。
-
已选择键集 是这样一种键的集合,即在前一次选择操作期间,检测每个键的通道是否已经至少为该键的相关操作集所标识的一个操作准备就绪。此集合由
selectedKeys
方法返回。已选择键集始终是键集的一个子集。
-
已取消键集 是已被取消但其通道尚未注销的键的集合。不可直接访问此集合。已取消键集始终是键集的一个子集。
在新创建的选择器中,这三个集合都是空集合。
通过某个通道的
register
方法注册该通道时,所带来的副作用是向选择器的键集中添加了一个键。在选择操作期间从键集中移除已取消的键。键集本身是不可直接修改的。
不管是通过关闭某个键的通道还是调用该键的
cancel
方法来取消键,该键都被添加到其选择器的已取消键集中。取消某个键会导致在下一次选择操作期间注销该键的通道,而在注销时将从所有选择器的键集中移除该键。
通过选择操作将键添加到已选择键集中。可通过调用已选择键集的
remove
方法,或者通过调用从该键集获得的
iterator
的
remove
方法直接移除某个键。通过任何其他方式从不会将键从已选择键集中移除;特别是,它们不会因为影响选择操作而被移除。不能将键直接添加到已选择键集中。
选择
在每次选择操作期间,都可以将键添加到选择器的已选择键集以及从中将其移除,并且可以从其键集和已取消键集中将其移除。选择是由
select()
、select(long)
和
selectNow()
方法执行的,执行涉及三个步骤:
-
将已取消键集中的每个键从所有键集中移除(如果该键是键集的成员),并注销其通道。此步骤使已取消键集成为空集。
-
在开始进行选择操作时,应查询基础操作系统来更新每个剩余通道的准备就绪信息,以执行由其键的相关集合所标识的任意操作。对于已为至少一个这样的操作准备就绪的通道,执行以下两种操作之一:
-
如果该通道的键尚未在已选择键集中,则将其添加到该集合中,并修改其准备就绪操作集,以准确地标识那些通道现在已报告为之准备就绪的操作。丢弃准备就绪操作集中以前记录的所有准备就绪信息。
-
如果该通道的键已经在已选择键集中,则修改其准备就绪操作集,以准确地标识所有通道已报告为之准备就绪的新操作。保留准备就绪操作集以前记录的所有准备就绪信息;换句话说,基础系统所返回的准备就绪操作集是和该键的当前准备就绪操作集按位分开 (bitwise-disjoined) 的。
如果在此步骤开始时键集中的所有键都有空的相关集合,则不会更新已选择键集和任意键的准备就绪操作集。
-
如果在步骤 (2) 的执行过程中要将任意键添加到已取消键集中,则处理过程如步骤 (1)。
是否阻塞选择操作以等待一个或多个通道准备就绪,如果这样做的话,要等待多久,这是三种选择方法之间的唯一本质差别。
并发性
选择器自身可由多个并发线程安全使用,但是其键集并非如此。
选择操作在选择器本身上、在键集上和在已选择键集上是同步的,顺序也与此顺序相同。在执行上面的步骤 (1) 和 (3) 时,它们在已取消键集上也是同步的。
在执行选择操作的过程中,更改选择器键的相关集合对该操作没有影响;进行下一次选择操作才会看到此更改。
可在任意时间取消键和关闭通道。因此,在一个或多个选择器的键集中出现某个键并不意味着该键是有效的,也不意味着其通道处于打开状态。如果存在另一个线程取消某个键或关闭某个通道的可能性,那么应用程序代码进行同步时应该小心,并且必要时应该检查这些条件。
阻塞在 select()
或
select(long)
方法之一中的某个线程可能被其他线程以下列三种方式之一中断:
close
方法在选择器上是同步的,并且所有三个键集都与选择操作中的顺序相同。
一般情况下,选择器的键和已选择键集由多个并发线程使用是不安全的。如果这样的线程可以直接修改这些键集之一,那么应该通过对该键集本身进行同步来控制访问。这些键集的
iterator
方法所返回的迭代器是快速失败 的:如果在创建迭代器后以任何方式(调用迭代器自身的
remove
方法除外)修改键集,则会抛出
ConcurrentModificationException
。
wakeup
public abstract Selector wakeup()
使尚未返回的第一个选择操作立即返回。
如果另一个线程目前正阻塞在 select()
或select(long)
方法的调用中,则该调用将立即返回。如果当前未进行选择操作,那么在没有同时调用selectNow()
方法的情况下,对上述方法的下一次调用将立即返回。在任一情况下,该调用返回的值可能是非零的。如果未同时再次调用此方法,则照常阻塞select()
或select(long)
方法的后续调用。
在两个连续的选择操作之间多次调用此方法与只调用一次的效果相同。
返回:
此选择器
selectNow
public abstract int selectNow()
throws IOException
选择一组键,其相应的通道已为 I/O 操作准备就绪。
此方法执行非阻塞的选择操作。如果自从前一次选择操作后,没有通道变成可选择的,则此方法直接返回零。
调用此方法会清除所有以前调用 wakeup
方法所得的结果。
返回:
由选择操作更新其准备就绪操作集的键的数目,该数目可能为零
抛出:
IOException
- 如果发生 I/O 错误
ClosedSelectorException
- 如果此选择器已关闭
select
public abstract int select()
throws IOException
选择一组键,其相应的通道已为 I/O 操作准备就绪。
此方法执行处于阻塞模式的选择操作。仅在至少选择一个通道、调用此选择器的
wakeup
方法,或者当前的线程已中断(以先到者为准)后此方法才返回。
返回:
已更新其准备就绪操作集的键的数目,该数目可能为零
抛出:
IOException
- 如果发生 I/O 错误
ClosedSelectorException
- 如果此选择器已关闭
一、 SelectionKey用完一定移除
Iterator<SelectionKey> it=selector.selectedKeys().iterator();
...for/while it.hasNext()...
it.remove();//键使用完成后必须这样做,或者Set.clear()也行; 否则cpu占用100%,特别是当客户端切断连接(channel)时。
移除完成以后一定要重新做一下这个操作:
selectionKey.interestOps(selectionKey.interestOps() & (~selectionKey.readyOps));
将事件删除,重新注册
二、Selector.open()不是线程安全的,可能抛出NullPointerException
bug地址:http://bugs.sun.com/view_bug.do?bug_id=6427854
synchronized (Selector.class) {
// Selector.open() isn't thread safe
// http://bugs.sun.com/view_bug.do?bug_id=6427854
// Affects 1.6.0_29, fixed in 1.7.0_01
SHARED_SELECTOR = Selector.open();
}
三、如果一个selection thread已经在select方法上等待,那么这个时候如果有另一条线程调用channal.register方法的话,那么它将被blocking.
四、selectionKey.cancel() BUG导致CPU占用100%
bug地址:http://bugs.sun.com/view_bug.do?bug_id=6403933
其原因就是调用key.cancel()时底层在下一次seelect前并没有真正的取消。导致等待select事件返回却又没有返回我们注册的key.这个事件不断地循环触发,CPU一直处理返回 key为0的select()调用。解决方法有两种,一是在key.cancel()后立即selectNow();但是如果是多线程并发操作,有可能这两行语句中间线程被切换,使得key.cancel()后没有立即执行selectNow().这在多Selector情况下是可能的。另一种就是jetty处理方式,如果select()返回0且连续几次出现这样的情况(有事件触发返回,却不是返回我们注册的KEY),就将有效的key重新注册到一个新的selector上。其实glassfish在处理多次次次次write返回为0的情况时也是这种策略。
tomcat7的处理方式:
try{
//对键的操作
}finally{
if (key != null) {
key.cancel();
if (selector != null)
selector.selectNow();// removes the key from this selector
}
}
Jetty的处理方式:
long before=now;
int selected=selector.select(wait);
now = System.currentTimeMillis();
_idleTimeout.setNow(now);
_timeout.setNow(now);
// Look for JVM bugs
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6403933
if (__JVMBUG_THRESHHOLD>0 && selected==0 && wait>__JVMBUG_THRESHHOLD && (now-before)<(wait/2) )
{
_jvmBug++;
if (_jvmBug>=(__JVMBUG_THRESHHOLD2))
{
synchronized (this)
{
_lastJVMBug=now;// BLOODY SUN BUG !!! Try refreshing the entire selector.
final Selector new_selector = Selector.open();
for (SelectionKey k: selector.keys())
{
if (!k.isValid() || k.interestOps()==0)
continue;
final SelectableChannel channel = k.channel();
final Object attachment = k.attachment();
if (attachment==null)
addChange(channel);
else
addChange(channel,attachment);
}
_selector.close();
_selector=new_selector;
_jvmBug=0;
return;
}
}
else if (_jvmBug==__JVMBUG_THRESHHOLD || _jvmBug==__JVMBUG_THRESHHOLD1)
{
// Cancel keys with 0 interested ops
for (SelectionKey k: selector.keys())
{
if (k.isValid()&&k.interestOps()==0)
{
k.cancel();
}
}
return;
}
}
else
_jvmBug=0;
五、(so) SocketChannels registered with OP_WRITE only release selector once (win) CPU 100%
bug地址:http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4469394
总结:使用JDK 6 U4以上版本不会出现以上bug(除“Selector.open()不是线程安全的”以外)。
分享到:
相关推荐
主要介绍了Java NIO实战之聊天室功能,结合实例形式详细分析了java NIO聊天室具体的服务端、客户端相关实现方法与操作注意事项,需要的朋友可以参考下
│ Java面试题84:项目流程和业务注意事项.mp4 │ 面试必问-Mysql索引背后的故事 │ ├─java面试专属 │ ├─1.面试必考之HashMap源码分析与实现 │ │ 1.面试必考之HashMap源码分析与实现.mp4 │ │ │ ├─2....
Apache MINA 是一个网络应用程序框架,用来帮助用户简单地开发高性能 和高可靠性的网络应用程序。它提供了一个通过Java NIO 在不同的传输例如 TCP/IP 和UDP/IP 上抽象的事件驱动的异步API。
Netty引用计数的实现机制与自旋锁的使用技巧 82_Netty引用计数原子更新揭秘与AtomicIntegerFieldUpdater深度剖析 83_AtomicIntegerFieldUpdater实例演练与volatile关键字分析 84_Netty引用计数注意事项与内存泄露...
资源内容: Java并行程序基础;Java内存模型和线程安全;并发设计模式;NIO和AIO;锁的优化和注意事项;JDK并发包
4锁的优化及注意事项 4.1有助于提高锁性能的几点建议 4.2java虚拟机对锁优化所做的努力 4.3人手一支笔:threadlocal 4.4无锁 4.5有关死锁的问题 4.2Java虚拟机对锁优化所做的努力 4.3人手一支笔:ThreadLocal 4.4...
84_Netty引用计数注意事项与内存泄露检测方式 85_Netty编解码器剖析与入站出站处理器详解 86_Netty自定义编解码器与TCP粘包拆包问题 87_Netty编解码器执行流程深入分析 88_ReplayingDecoder源码分析与特性解读 89_...
16:55:20.675 [main] INFO org.malin.allutils.makefile.ReadFileNameUtil - 获取到 文件名称: 10.并发调试和JDK8新特性.pdf 16:55:20.677 [main] INFO org.malin.allutils.makefile....锁的优化和注意事项.pdf
第84讲:Netty引用计数注意事项与内存泄露检测方式 第85讲:Netty编解码器剖析与入站出站处理器详解 第86讲:Netty自定义编解码器与TCP粘包拆包问题 第87讲:Netty编解码器执行流程深入分析 第88讲:...
leetcode题库 1 2 3 4 5 6 算法 目录根据原书第二版进行编排,代码和原书有所不同,尽量比原...JavaIO知识,字符流,字节流,Socket,NIO。 操作系统 网络 面向对象 Gof 的 23 种设计模式。 数据库 框架 系统设计 工具
制作节目时要注意的事项,请按照以下步骤进行操作:制作节目时要注意的事项,包括制作,播放和播放节目时所用的字幕。 Minhaintençãonesseéreativar tal projeto,inserir o novo paradigma,increir ...
注意事项: 1. 必须要关闭IO流,节约资源开销 2. 关闭IO流原则,先开后关 3. IO流工具类的抽取,将所有的关流(字节流和字符流)方法抽取出来,优化代码 4. 字符流:(重点) * 使用场景...