选择器的并发性

4.3.4 并发性

  选择器对象是线程安全的,但它们包含的键集合不是。通过keys( )和selectKeys( )返回的键的集合是Selector对象内部的私有的Set对象集合的直接引用。这些集合可能在任意时间被改变。已注册的键的集合是只读的。如果您试图修改它,那么您得到的奖品将是一个java.lang.UnsupportedOperationException,但是当您在观察它们的时候,它们可能发生了改变的话,您仍然会遇到麻烦。Iterator对象是快速失败的(fail-fast):如果底层的Set被改变了,它们将会抛出java.util.ConcurrentModificationException,因此如果您期望在多个线程间共享选择器和/或键,请对此做好准备。您可以直接修改选择键,但请注意您这么做时可能会彻底破坏另一个线程的Iterator。

  如果在多个线程并发地访问一个选择器的键的集合的时候存在任何问题,您可以采取一些步骤来合理地同步访问。在执行选择操作时,选择器在Selector对象上进行同步,然后是已注册的键的集合,最后是已选择的键的集合,按照这样的顺序。已取消的键的集合也在选择过程的的第1步和第3步之间保持同步(当与已取消的键的集合相关的通道被注销时)

  在多线程的场景中,如果您需要对任何一个键的集合进行更改,不管是直接更改还是其他操作带来的副作用,您都需要首先以相同的顺序,在同一对象上进行同步。锁的过程是非常重要的。如果竞争的线程没有以相同的顺序请求锁,就将会有死锁的潜在隐患。如果您可以确保否其他线程不会同时访问选择器,那么就不必要进行同步了。

  Selector类的close( )方法与slect( )方法的同步方式是一样的,因此也有一直阻塞的可能性。在选择过程还在进行的过程中,所有对close( )的调用都会被阻塞,直到选择过程结束,或者执行选择的线程进入睡眠。在后面的情况下,执行选择的线程将会在执行关闭的线程获得锁是立即被唤醒,并关闭选择器(参见4.3.2小节)。

4.4 异步可关闭性

  任何时候都有可能关闭一个通道或者取消一个选择键。除非您采取步骤进行同步,否则键的状态及相关的通道将发生意料之外的改变。一个特定的键的集合中的一个键的存在并不保证键仍然是有效的,或者它相关的通道仍然是打开的。

  关闭通道的过程不应该是一个耗时的操作。NIO的设计者们特别想要阻止这样的可能性:一个线程在关闭一个处于选择操作中的通道时,被阻塞于无限期的等待。当一个通道关闭时,它相关的键也就都被取消了。这并不会影响正在进行的select( ),但这意味着在您调用select( )之前仍然是有效的键,在返回时可能会变为无效。您总是可以使用由选择器的selectKeys( )方法返回的已选择的键的集合:请不要自己维护键的集合。理解3.4.5小节描述的选择过程,对于避免遇到问题而言是非常重要的。

  您可以参考4.3.2小节,以详细了解一个在select( )中阻塞的线程是如何被唤醒的。如果您试图使用一个已经失效的键,大多数方法将抛出CancelledKeyException。但是,您可以安全地从从已取消的键中获取通道的句柄。如果通道已经关闭时,仍然试图使用它的话,在大多数情况下将引发ClosedChannelException

 

4.5 选择过程的可扩展性

  我多次提到选择器可以简化用单线程同时管理多个可选择通道的实现。使用一个线程来为多个通道提供服务,通过消除管理各个线程的额外开销,可能会降低复杂性并可能大幅提升性能。但只使用一个线程来服务所有可选择的通道是否是一个好主意呢?这要看情况。

  对单CPU的系统而言这可能是一个好主意,因为在任何情况下都只有一个线程能够运行。通过消除在线程之间进行上下文切换带来的额外开销,总吞吐量可以得到提高。但对于一个多CPU的系统呢?在一个有n个CPU的系统上,当一个单一的线程线性地轮流处理每一个线程时,可能有n-1个cpu处于空闲状态

  那么让不同道请求不同的服务类的办法如何?想象一下,如果一个应用程序为大量的分布式的传感器记录信息。每个传感器在服务线程遍历每个就绪的通道时需要等待数秒钟。这在响应时间不重要时是可以的。但对于高优先级的连接(如操作命令),如果只用一个线程为所有通道提供服务,将不得不在队列中等待。不同的应用程序的要求也是不同的。您采用的策略会受到您尝试解决的问题的影响。

  在第一个场景中,如果您想要将更多的线程来为通道提供服务,请抵抗住使用多个选择器的欲望。在大量通道上执行就绪选择并不会有很大的开销,大多数工作是由底层操作系统完成的。管理多个选择器并随机地将通道分派给它们当中的一个并不是这个问题的合理的解决方案。这只会形成这个场景的一个更小的版本。一个更好的策略是对所有的可选择通道使用一个选择器,并将对就绪通道的服务委托给其他线程。您只用一个线程监控通道的就绪状态并使用一个协调好的工作线程池来处理共接收到的数据。根据部署的条件,线程池的大小是可以调整的(或者它自己进行动态的调整)。对可选择通道的管理仍然是简单的,而简单的就是好的。

  第二个场景中,某些通道要求比其他通道更高的响应速度,可以通过使用两个选择器来解决:一个为命令连接服务,另一个为普通连接服务。但这种场景也可以使用与第一个场景十分相似的办法来解决。与将所有准备好的通道放到同一个线程池的做法不同,通道可以根据功能由不同的工作线程来处理。它们可能可以是日志线程池,命令/控制线程池,状态请求线程池,等等。

  例 4-2的代码是例4-1的一般性的选择循环的扩展。它覆写了readDataFromSocket( )方法,并使用线程池来为准备好数据用于读取的通道提供服务。与在主线程中同步地读取数据不同,这个版本的实现将SelectionKey对象传递给为其服务的工作线程。

 

例 4-2. 使用线程池来为通道提供服务

 

/*** */
package test.noi.select;import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;/*** * Simple echo-back server which listens for incoming stream connections and* * echoes back whatever it reads. A single Selector object is used to listen to* * the server socket (to accept new connections) and all the active socket* * channels.* * @author Ron Hitchens (ron@ronsoft.com)*/
public class SelectSockets {public static int PORT_NUMBER = 1234;public static void main(String[] argv) throws Exception {new SelectSockets().go(argv);}public void go(String[] argv) throws Exception {int port = PORT_NUMBER;if (argv.length > 0) {// Override default listen portport = Integer.parseInt(argv[0]);}System.out.println("Listening on port " + port);ServerSocketChannel serverChannel = ServerSocketChannel.open();// Get the associated ServerSocket to bind it withServerSocket serverSocket = serverChannel.socket();// Create a new Selector for use belowSelector selector = Selector.open();// Set the port the server channel will listen toserverSocket.bind(new InetSocketAddress(port));// Set nonblocking mode for the listening socketserverChannel.configureBlocking(false);// Register the ServerSocketChannel with the Selector
        serverChannel.register(selector, SelectionKey.OP_ACCEPT);while (true) {// This may block for a long time. Upon returning, the// selected set contains keys of the ready channels.int n = selector.select();if (n == 0) {continue;// nothing to do
            }// Get an iterator over the set of selected keysIterator it = selector.selectedKeys().iterator();// Look at each key in the selected setwhile (it.hasNext()) {SelectionKey key = (SelectionKey) it.next();// Is a new connection coming in?if (key.isAcceptable()) {ServerSocketChannel server = (ServerSocketChannel) key.channel();SocketChannel channel = server.accept();registerChannel(selector, channel, SelectionKey.OP_READ);sayHello(channel);}// Is there data to read on this channel?if (key.isReadable()) {readDataFromSocket(key);}// Remove key from selected set; it's been handled
                it.remove();}}}/*** * Register the given channel with the given selector for the given ** operations of interest*/protected void registerChannel(Selector selector,SelectableChannel channel, int ops) throws Exception {if (channel == null) {return; // could happen}// Set the new channel nonblockingchannel.configureBlocking(false);// Register it with the selector
        channel.register(selector, ops);}// ----------------------------------------------------------// Use the same byte buffer for all channels. A single thread is// servicing all the channels, so no danger of concurrent acccess.private ByteBuffer buffer = ByteBuffer.allocateDirect(1024);/*** * Sample data handler method for a channel with data ready to read. * * @param* key * A SelectionKey object associated with a channel determined by * the* selector to be ready for reading. If the channel returns* * * an EOF condition, it is closed here, which automatically * invalidates* the associated key. The selector will then * de-register the channel on* the next select call.*/protected void readDataFromSocket(SelectionKey key) throws Exception {SocketChannel socketChannel = (SocketChannel) key.channel();int count;buffer.clear();// Empty buffer// Loop while data is available;channel is nonblockingwhile ((count = socketChannel.read(buffer)) > 0) {buffer.flip();// Make buffer readable// Send the data; don't assume it goes all at oncewhile (buffer.hasRemaining()) {socketChannel.write(buffer);}// WARNING: the above loop is evil. Because// it's writing back to the same nonblocking// channel it read the data from, this code can// potentially spin in a busy loop. In real life// you'd do something more useful than this.
            buffer.clear();// Empty buffer
        }if (count < 0) {// Close channel on EOF, invalidates the key
            socketChannel.close();}}/*** * Spew a greeting to the incoming client connection. * * @param channel ** The newly connected SocketChannel to say hello to.*/private void sayHello(SocketChannel channel) throws Exception {buffer.clear();buffer.put("Hi there!\r\n".getBytes());buffer.flip();channel.write(buffer);}
}

 

 

 

  由于执行选择过程的线程将重新循环并几乎立即再次调用select( ),键的interest集合将被修改,并将interest(感兴趣的操作)从读取就绪(read-rreadiness)状态中移除。这将防止选择器重复地调用readDataFromSocket( )(因为通道仍然会准备好读取数据,直到工作线程从它那里读取数据)。当工作线程结束为通道提供的服务时,它将再次更新键的ready集合,来将interest重新放到读取就绪集合中。它也会在选择器上显式地调用wakeup( )。如果主线程在select( )中被阻塞,这将使它继续执行。这个选择循环会再次执行一个轮回(可能什么也没做)并带着被更新的键重新进入select( )。

 

 

 

以上内容出自 nio 一书

 

转载于:https://www.cnblogs.com/mjorcen/p/4203867.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/273464.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql 自定义函数之判断

DELIMITER $$CREATE DEFINERrootlocalhost FUNCTION getMin(a int,b int) RETURNS int(11)BEGINdeclare min int;if(a>b)then set min b;elseif(b>a)then set min a;else set min 0;#end if;end if;RETURN min;END 调用该函数可以如下方式 select getMin(1,2); 返回值…

C/C++的数组名

数组名相当于指向数组第一个元素的地址。数组名不是变量&#xff0c;是地址常量&#xff0c;不能为其赋值。如下&#xff1a;1&#xff09;一维数组中对于数组 a[5] {1, 2, 3, 4, 5};数组名a相当于指向第一个元素a[0]的指针。即 a 与 &a[0] 等价。2&#xff09;二维数组中…

mysql的运算法

一、算术运算符1、加 www.2cto.com mysql> select 12;-----| 12 |-----| 3 |-----2、减mysql> select 1-2;-----| 1-2 |-----| -1 |-----3、乘mysql> select 2*3;-----| 2*3 |-----| 6 |-----4、除mysql> select 2/3;--------| 2/3 |--------| 0.6667 |-…

转-- iOS 30多个iOS常用动画,带详细注释

// // CoreAnimationEffect.h // CoreAnimationEffect // // Created by VincentXue on 13-1-19. // Copyright (c) 2013年 VincentXue. All rights reserved. //#import <Foundation/Foundation.h>/**! 导入QuartzCore.framework** Example:** Step.1** #imp…

Java中abstract与interface

抽象类&#xff08;abstract class&#xff09;的特点&#xff1a; 1.抽象类、抽象方法都必须使用abstract修饰。 2.抽象类中&#xff0c;可以有非抽象方法&#xff0c;甚至可以是没有任何方法或变量的空类。 对于抽象类中不定义抽象方法的用意在于&#xff1a;使该类不能被创建…

按位与、或、异或等运算方法

按位与运算符&#xff08;&&#xff09; 参加运算的两个数据&#xff0c;按二进制位进行“与”运算。 运算规则&#xff1a;0&00; 0&10; 1&00; 1&11; 即&#xff1a;两位同时为“1”&#xff0c;结果才为“1”&#xff0c;否则为0 例如&#xff1…

JavaScript验证

<script type"text/javascript"> /*密码*/ function password() { var password document.getElementById("password").value; var ts document.getElementById("tsPassword"); if (password.length >…

mysql数据库根据上传的经纬度计算距离

select 6371.393*ACOS(COS(RADIANS(latitude))*COS(RADIANS(47.02))*COS(RADIANS(longitude)-RADIANS(114.100))SIN(RADIANS(latitude))*SIN(RADIANS(47.02))) as distancefrom location

emacs配置

; 指针颜色设置为白色(set-cursor-color "white");; 鼠标颜色设置为白色(set-mouse-color "white") ;; 从color-theme中获取;; 网上下载color-theme.el&#xff0c;放到加载路径(&#xff0f;usr/share/emacs/site-lisp )下;; M-x color-theme-select,鼠标…

自然连接(NATURAL JOIN)

自然连接&#xff08;NATURAL JOIN&#xff09;是一种特殊的等价连接&#xff0c;它将表中具有相同名称的列自动进行记录匹配。自然连接不必指定任何同等连接条件。图9.9给出了典型的自然连接示意图。 图9.9 自然连接 自然连接自动判断相同名称的列&#xff0c;而后形成匹配。…

iis express8 自动关闭

引用&#xff1a;http://www.cnblogs.com/chunCui/p/3522619.html 问题&#xff1a;最近使用vs2013开发个web &#xff0c; 每次调试结束时iis express 8 也会自动关闭 解决方法&#xff1a;web项目-属性-web-调试器-只选中ASP.Net就可以了 转载于:https://www.cnblogs.com/qqq…

自连接

9.3 表的连接类型 9.3.1 自连接 自连接是指表与其自身进行连接&#xff0c;这就需要用到前面介绍的表别名。下面通过一个具体实例来讲解自连接的应用。 实例5 自连接的使用方法 查询成绩中存在不及格课程的学生的姓名、所在系、所有的课程及成绩信息。如果采用前面介绍的…

从此记录

从此记录工作、学习、生活的那些事儿&#xff01;转载于:https://www.cnblogs.com/alwaysjava/p/4221362.html

LIKE运算符

6.5 使用LIKE进行模糊查询 当只知道部分字符串时&#xff0c;可使用LIKE运算符来查询数据库&#xff0c;找出与其相关的整个字符串。因此&#xff0c;当把关键字LIKE用在WHERE子句中时&#xff0c;可以比较两个字符串的部分匹配。当对字符串内容有些印象&#xff0c;但并不知…

AND运算符

6.2 组合查询条件 在前一章提到的WHERE子句进行查询时&#xff0c;WHERE子句后面的搜索条件只是单一的。实际上&#xff0c;可以通过布尔运算符AND和OR&#xff0c;将多个单独的搜索条件结合在一个WHERE子句中&#xff0c;形成一个复合的搜索条件。当对复合搜索条件求值时&a…

Cron表达式【一】

Cron表达式【一】 Cron表达式被用来配置CronTrigger实例。 Cron表达式是一个由 7个子表达式组成的字符串。每个子表达式都描述了一个单独的日程细节。这些子表达式用空格分隔&#xff0c;分别表示&#xff1a; 1. Seconds 秒 2. Minutes 分钟 3. Hours 小时 4. Day-of-Month 月…

OR运算符

6.2.2 OR运算符 OR运算符表示“或”的关系。当可能有多个条件为True&#xff0c;但只要有一个为True就满足搜索要求时&#xff0c;可以使用OR运算符来组合搜索条件。OR在结合两个布尔表达式时&#xff0c;只要其中一个条件为True时&#xff0c;便传回True。OR运算符的真值表…

Java基础---网络编程

第一讲 概述 1、网络模型&#xff1a;OSI参考模型和TCP/IP参考模型 图示&#xff1a; 一般来说开发处于传输层和网际层&#xff0c;应用层为&#xff1a;FTP和HTTP协议等&#xff0c;传输层为&#xff1a;UDP和TCP等&#xff0c;网际层为&#xff1a;IP。 通常用户操作的是…

AND、OR运算符的组合使用

6.2.3 AND、OR运算符的组合使用 在WHERE子句中&#xff0c;通过AND、OR运算符可以同时连接多个条件&#xff0c;当然AND、OR运算符也可以同时使用。但是当AND、OR运算符同时存在时&#xff0c;其优先级如何确定呢&#xff1f;与大多数语言一样&#xff0c;SQL语言认为AND运算…

Nginx配置指定媒体类型文件强制下载

由于业务需要&#xff0c;在点击显示链接&#xff08;如www.xxx.com/2015-01-15/xxx.png&#xff09;显示媒体资源&#xff08;如图片、视频、音频、文档&#xff09;&#xff0c;而在点击下载链接&#xff08;如www.xxx.com/2015-01-15/xxx.png?downloadtrue&#xff09;请求…