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) 方法之一中的某个线程可能被其他线程以下列三种方式之一中断:
通过调用选择器的 wakeup 方法,
通过调用选择器的 close 方法,或者
在通过调用已阻塞线程的 interrupt 方法的情况下,将设置其中断状态并且将调用该选择器的 wakeup 方法。
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)时 。
移除完成以后一定要重新做一下这个操作:
[java] view plaincopy
selectionKey.interestOps(selectionKey.interestOps() & (~selectionKey.readyOps));
将事件删除,重新注册
二、Selector.open()不是线程安全的,可能抛出NullPointerException
bug地址:http://bugs.sun.com/view_bug.do?bug_id=6427854
[java] view plaincopy
synchronized (Selector.class) {
// Selector.open() isn't thread safe
// <a href="http://bugs.sun.com/view_bug.do?bug_id=6427854 //">http://bugs.sun.com/view_bug.do?bug_id=6427854
//</a> 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的处理方式:
[java] view plaincopy
try{
//对键的操作
}finally{
if (key != null) {
key.cancel();
if (selector != null)
selector.selectNow();// removes the key from this selector
}
}
Jetty的处理方式:
[java] view plaincopy
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()不是线程安全的”以外)。
转:http://blog.csdn.net/wangxi969696/article/details/7352978
分享到:
相关推荐
readme.txt文件通常包含了工具的使用说明和注意事项,是了解工具功能和正确使用的关键。 总的来说,"默蓝网络通信测试工具(NIOSocket工具)"凭借其对TCP/IP和HTTP通信的支持,以及Java NIO Socket编程的优势,为网络...
- **主要内容**:总结NIO编程的一般步骤和注意事项。 - **学习目标**:形成系统性的NIO编程思路。 #### 27. Java NIO-Pipe-介绍和代码示例 - **主要内容**:介绍Pipe(管道)的概念及其使用方法,并提供示例代码。 ...
Java NIO实战之聊天室功能详解主要介绍了Java NIO实战之聊天室功能,结合实例形式详细分析了Java NIO聊天室具体的服务端、客户端相关实现方法与操作注意事项。 Java NIO概述 Java NIO(New I/O)是一种Java API,...
8. **注意事项** - 虽然异步模式能提高性能,但可能导致编程复杂性增加,需要妥善处理回调地狱问题。 - 确保正确处理网络异常,如超时、连接中断等,保证系统的健壮性。 通过这个自定义的Java Socket开发框架...
9. **安全注意事项** - 定期更新密钥和证书,防止被破解。 - 使用最新的SSL/TLS版本,避免已知的安全漏洞。 - 对用户输入进行校验,防止注入攻击。 通过以上内容,你应该对Java中SSLSocket的应用有了基本的理解...
### 注意事项 - 在MINA中使用IoFilter来处理一些共用的、与业务逻辑无关的功能,可以使得应用结构更加清晰,便于维护和扩展。 - 应用程序中重载`IoHandler`的事件方法来响应客户端的不同事件。例如`messageReceived...
以上就是Java中利用Socket实现全双工通信的基本概念、步骤和注意事项。实际开发中,可能需要结合多线程、NIO等技术,以提高并发处理能力和性能。理解并熟练掌握Socket编程,对于进行网络应用的开发至关重要。
7. **程序说明.txt**: 这个文件通常会包含如何编译和运行程序的指令,以及可能的注意事项和故障排查步骤。例如,可能需要指定服务器的IP地址和端口号,或者解释如何启动服务器和连接的客户端。 在实际的聊天程序...
五、注意事项 1. 异常处理:网络编程中可能出现IOException,如连接失败、数据传输错误等,需要妥善处理。 2. 流的关闭:为避免资源泄露,每次使用完输入输出流后应及时关闭。 3. 阻塞与非阻塞:默认情况下,Socket...
**注意事项:** 1. 异步处理:在实际应用中,服务端可能需要同时处理多个客户端连接,可以使用多线程或者Java NIO来实现非阻塞I/O。 2. 错误处理:确保对IOException和其他可能的异常进行妥善处理,避免程序中断。 3...
**七、注意事项** 1. **端口选择**:确保使用的端口号未被其他服务占用,一般1024以下的端口需要管理员权限。 2. **防火墙设置**:可能需要调整防火墙规则,允许特定端口的通信。 3. **连接超时**:设置Socket的...
5. 开发注意事项: - 确保遵循CMPP协议的报文格式,避免数据错乱或丢失。 - 考虑到网络环境的不稳定,需要有良好的错误处理和重试机制。 - 对于高并发场景,考虑优化并发处理和资源利用率。 总结,Java CMPP3.0...
六、注意事项与法律问题 端口扫描可能被视为不道德或非法行为,因此在进行任何扫描前,请确保你有合法的理由并遵循相关法律法规。 总结,Java开发中的端口扫描涉及到TCP连接、UDP通信、多线程和异步编程等技术。...
本篇文章将深入探讨如何在Android中利用Socket进行基于TCP协议的网络通信,结合提供的"TestMyServerSocket"源码,我们将解析其核心概念、步骤以及注意事项。 1. **Socket基础** Socket,也称为套接字,是网络通信...
使用须知.txt文件可能包含了如何使用Smart-Socket框架的关键步骤和注意事项,例如: 1. **安装与引入**:首先需要将Smart-Socket的jar包引入到项目中,可以通过Maven或Gradle等构建工具添加依赖。 2. **服务端配置*...
4. **注意事项**: - 在局域网环境中,确保服务器的IP地址是局域网内其他设备可以访问的,通常是通过`InetAddress.getLocalHost().getHostAddress()`获取。 - 为了防止端口被占用,每次运行前应检查端口是否可用,...
通过以上步骤和注意事项,我们可以创建出能在Android设备上运行的基于WiFi的Socket客户端和服务器端应用程序,实现两端之间的数据传输。在"SocketServer"和"SocketClient"的源代码中,你可以看到这些概念是如何具体...
通过上述步骤和注意事项,你可以构建基本的Java Socket通信程序。然而,实际开发中可能还需要处理更复杂的情况,如心跳机制、断线重连、流量控制等。了解这些知识对于成为一名熟练的Java网络程序员至关重要。
6. 注意事项: - 由于Android系统的网络限制,通常需要在主线程之外进行Socket操作,避免造成应用卡顿。 - 服务器端需要持续运行,确保随时能响应客户端的连接请求。 以上就是关于Android应用与PC之间Socket通信...