出处:Java NIO Selector

1.1. Selector入门

1.1.1. Selector的和Channel的关系

 Java NIO的核心组件包括:

  (1)Channel(通道)

  (2)Buffer(缓冲区)

  (3)Selector(选择器)

  其中Channel和Buffer比较好理解 ,联系也比较密切,他们的关系简单来说就是:数据总是从通道中读到buffer缓冲区内,或者从buffer写入到通道中。

 选择器和他们的关系又是什么?

  选择器(Selector) 是 Channel(通道)的多路复用器,Selector 可以同时监控多个 通道的 IO(输入输出) 状况。

 Selector的作用是什么?

  选择器提供选择执行已经就绪的任务的能力。从底层来看,Selector提供了询问通道是否已经准备好执行每个I/O操作的能力。Selector 允许单线程处理多个Channel。仅用单个线程来处理多个Channels的好处是,只需要更少的线程来处理通道。事实上,可以只用一个线程处理所有的通道,这样会大量的减少线程之间上下文切换的开销。

1.1.2. 可选择通道(SelectableChannel)

  并不是所有的Channel,都是可以被Selector 复用的。比方说,FileChannel就不能被选择器复用。为什么呢?

  判断一个Channel 能被Selector 复用,有一个前提:判断他是否继承了一个抽象类SelectableChannel。如果继承了SelectableChannel,则可以被复用,否则不能。

 SelectableChannel类是何方神圣?

  SelectableChannel类提供了实现通道的可选择性所需要的公共方法。它是所有支持就绪检查的通道类的父类。所有socket通道,都继承了SelectableChannel类都是可选择的,包括从管道(Pipe)对象的中获得的通道。而FileChannel类,没有继承SelectableChannel,因此是不是可选通道。

 通道和选择器注册之后,他们是绑定的关系吗?

  答案是不是。不是一对一的关系。一个通道可以被注册到多个选择器上,但对每个选择器而言只能被注册一次。

  通道和选择器之间的关系,使用注册的方式完成。SelectableChannel可以被注册到Selector对象上,在注册的时候,需要指定通道的哪些操作,是Selector感兴趣的。

1.1.3. Channel注册到Selector

  使用Channel.register(Selector sel,int ops)方法,将一个通道注册到一个选择器时。第一个参数,指定通道要注册的选择器是谁。第二个参数指定选择器需要查询的通道操作。

 可以供选择器查询的通道操作,从类型来分,包括以下四种:

  (1)可读 : SelectionKey.OP_READ

  (2)可写 : SelectionKey.OP_WRITE

  (3)连接 : SelectionKey.OP_CONNECT

  (4)接收 : SelectionKey.OP_ACCEPT

  如果Selector对通道的多操作类型感兴趣,可以用“位或”操作符来实现:int key = SelectionKey.OP_READ | SelectionKey.OP_WRITE ;

  注意,操作一词,是一个是使用非常泛滥,也是一个容易混淆的词。特别提醒的是,选择器查询的不是通道的操作,而是通道的某个操作的一种就绪状态。

 什么是操作的就绪状态?

  一旦通道具备完成某个操作的条件,表示该通道的某个操作已经就绪,就可以被Selector查询到,程序可以对通道进行对应的操作。比方说,某个SocketChannel通道可以连接到一个服务器,则处于“连接就绪”(OP_CONNECT)。再比方说,一个ServerSocketChannel服务器通道准备好接收新进入的连接,则处于“接收就绪”(OP_ACCEPT)状态。还比方说,一个有数据可读的通道,可以说是“读就绪”(OP_READ)。一个等待写数据的通道可以说是“写就绪”(OP_WRITE)。

1.1.4. 选择键(SelectionKey)

  Channel和Selector的关系确定好后,并且一旦通道处于某种就绪的状态,就可以被选择器查询到。这个工作,使用选择器Selector的select()方法完成。select方法的作用,对感兴趣的通道操作,进行就绪状态的查询。

  Selector可以不断的查询Channel中发生的操作的就绪状态。并且挑选感兴趣的操作就绪状态。一旦通道有操作的就绪状态达成,并且是Selector感兴趣的操作,就会被Selector选中,放入选择键集合中。

  一个选择键,首先是包含了注册在Selector的通道操作的类型,比方说SelectionKey.OP_READ。也包含了特定的通道与特定的选择器之间的注册关系。

  开发应用程序是,选择键是编程的关键。NIO的编程,就是根据对应的选择键,进行不同的业务逻辑处理。

  选择键的概念,有点儿像事件的概念。

 选择键和事件的关系是什么?

  一个选择键有点儿像监听器模式里边的一个事件,但是又不是。由于Selector不是事件触发的模式,而是主动去查询的模式,所以不叫事件Event,而是叫SelectionKey选择键。

1.2. Selector的使用流程

1.2.1. 创建Selector

  Selector对象是通过调用静态工厂方法open()来实例化的,如下:

  1. // 1、获取Selector选择器
  2. Selector selector = Selector.open();

  Selector的类方法open()内部是向SPI发出请求,通过默认的SelectorProvider对象获取一个新的实例。

1.2.2. 将Channel注册到Selector

  要实现Selector管理Channel,需要将channel注册到相应的Selector上,如下:

  1. // 2、获取通道
  2. ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
  3. // 3.设置为非阻塞
  4. serverSocketChannel.configureBlocking(false);
  5. // 4、绑定连接
  6. serverSocketChannel.bind(new InetSocketAddress(SystemConfig.SOCKET_SERVER_PORT));
  7. // 5、将通道注册到选择器上,并制定监听事件为:“接收”事件
  8. serverSocketChannel.register(selectorSelectionKey.OP_ACCEPT);

  上面通过调用通道的register()方法会将它注册到一个选择器上。

 首先需要注意的是:

  与Selector一起使用时,Channel必须处于非阻塞模式下,否则将抛出异常IllegalBlockingModeException。这意味着,FileChannel不能与Selector一起使用,因为FileChannel不能切换到非阻塞模式,而套接字相关的所有的通道都可以。

 另外,还需要注意的是:

  一个通道,并没有一定要支持所有的四种操作。比如服务器通道ServerSocketChannel支持Accept 接受操作,而SocketChannel客户端通道则不支持。可以通过通道上的validOps()方法,来获取特定通道下所有支持的操作集合。

1.2.3. 轮询查询就绪操作

  万事俱备,可以开干。下一步是查询就绪的操作。

  通过Selector的select()方法,可以查询出已经就绪的通道操作,这些就绪的状态集合,包存在一个元素是SelectionKey对象的Set集合中。

  下面是Selector几个重载的查询select()方法:

    (1)select():阻塞到至少有一个通道在你注册的事件上就绪了。

    (2)select(long timeout):和select()一样,但最长阻塞事件为timeout毫秒。

    (3)selectNow():非阻塞,只要有通道就绪就立刻返回。

  select()方法返回的int值,表示有多少通道已经就绪,更准确的说,是自前一次select方法以来到这一次select方法之间的时间段上,有多少通道变成就绪状态。

  一旦调用select()方法,并且返回值不为0时,下一步工干啥?

  通过调用Selector的selectedKeys()方法来访问已选择键集合,然后迭代集合的每一个选择键元素,根据就绪操作的类型,完成对应的操作:

  1. Set selectedKeys = selector.selectedKeys();
  2. Iterator keyIterator = selectedKeys.iterator();
  3. while(keyIterator.hasNext()) {
  4. SelectionKey key = keyIterator.next();
  5. if(key.isAcceptable()) {
  6. // a connection was accepted by a ServerSocketChannel.
  7. } else if (key.isConnectable()) {
  8. // a connection was established with a remote server.
  9. } else if (key.isReadable()) {
  10. // a channel is ready for reading
  11. } else if (key.isWritable()) {
  12. // a channel is ready for writing
  13. }
  14. keyIterator.remove();
  15. }

  处理完成后,直接将选择键,从这个集合中移除,防止下一次循环的时候,被重复的处理。键可以但不能添加。试图向已选择的键的集合中添加元素将抛出java.lang.UnsupportedOperationException。

1.3. 一个NIO 编程的简单实例

  1. import com.crazymakercircle.config.SystemConfig;
  2.  
  3. import java.io.IOException;
  4. import java.net.InetSocketAddress;
  5. import java.nio.ByteBuffer;
  6. import java.nio.channels.SelectionKey;
  7. import java.nio.channels.Selector;
  8. import java.nio.channels.ServerSocketChannel;
  9. import java.nio.channels.SocketChannel;
  10. import java.util.Iterator;
  11.  
  12. public class SelectorDemo
  13. {
  14.  
  15. static class Client
  16. {
  17. /**
  18. * 客户端
  19. */
  20. public static void testClient() throws IOException
  21. {
  22. InetSocketAddress address= new InetSocketAddress(SystemConfig.SOCKET_SERVER_IP, SystemConfig.SOCKET_SERVER_PORT);
  23.  
  24. // 1、获取通道(channel)
  25. SocketChannel socketChannel = SocketChannel.open(address);
  26. // 2、切换成非阻塞模式
  27. socketChannel.configureBlocking(false);
  28.  
  29. // 3、分配指定大小的缓冲区
  30. ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
  31. byteBuffer.put("hello world ".getBytes());
  32. byteBuffer.flip();
  33. socketChannel.write(byteBuffer);
  34.  
  35. socketChannel.close();
  36. }
  37.  
  38. public static void main(String[] args) throws IOException
  39. {
  40. testClient();
  41. }
  42. }
  43. static class Server
  44. {
  45.  
  46. public static void testServer() throws IOException
  47. {
  48.  
  49. // 1、获取Selector选择器
  50. Selector selector = Selector.open();
  51.  
  52. // 2、获取通道
  53. ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
  54. // 3.设置为非阻塞
  55. serverSocketChannel.configureBlocking(false);
  56. // 4、绑定连接
  57. serverSocketChannel.bind(new InetSocketAddress(SystemConfig.SOCKET_SERVER_PORT));
  58.  
  59. // 5、将通道注册到选择器上,并注册的操作为:“接收”操作
  60. serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
  61.  
  62. // 6、采用轮询的方式,查询获取“准备就绪”的注册过的操作
  63. while (selector.select() > 0)
  64. {
  65. // 7、获取当前选择器中所有注册的选择键(“已经准备就绪的操作”)
  66. Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
  67. while (selectedKeys.hasNext())
  68. {
  69. // 8、获取“准备就绪”的时间
  70. SelectionKey selectedKey = selectedKeys.next();
  71.  
  72. // 9、判断key是具体的什么事件
  73. if (selectedKey.isAcceptable())
  74. {
  75. // 10、若接受的事件是“接收就绪” 操作,就获取客户端连接
  76. SocketChannel socketChannel = serverSocketChannel.accept();
  77. // 11、切换为非阻塞模式
  78. socketChannel.configureBlocking(false);
  79. // 12、将该通道注册到selector选择器上
  80. socketChannel.register(selector, SelectionKey.OP_READ);
  81. }
  82. else if (selectedKey.isReadable())
  83. {
  84. // 13、获取该选择器上的“读就绪”状态的通道
  85. SocketChannel socketChannel = (SocketChannel) selectedKey.channel();
  86.  
  87. // 14、读取数据
  88. ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
  89. int length = 0;
  90. while ((length = socketChannel.read(byteBuffer)) != -1)
  91. {
  92. byteBuffer.flip();
  93. System.out.println(new String(byteBuffer.array(), 0, length));
  94. byteBuffer.clear();
  95. }
  96. socketChannel.close();
  97. }
  98.  
  99. // 15、移除选择键
  100. selectedKeys.remove();
  101. }
  102. }
  103.  
  104. // 7、关闭连接
  105. serverSocketChannel.close();
  106. }
  107.  
  108. public static void main(String[] args) throws IOException
  109. {
  110. testServer();
  111. }
  112. }
  113. }

(四:NIO系列) Java NIO Selector的更多相关文章

  1. 【Java nio】java nio笔记

    缓冲区操作:缓冲区,以及缓冲区如何工作,是所有I/O的基础.所谓“输入/输出”讲的无非就是把数据移出货移进缓冲区.进程执行I/O操作,归纳起来也就是向操作系统发出请求,让它要么把缓冲区里的数据排干,要 ...

  2. Java-杂项-java.nio:java.nio

    ylbtech-Java-杂项-java.nio:java.nio java.nio全称java non-blocking IO,是指jdk1.4 及以上版本里提供的新api(New IO) ,为所有 ...

  3. java的nio之:java的nio系列教程之selector

    一:Java NIO的selector的概述===>Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件.这样,一个单独的线程 ...

  4. 【NIO】Java NIO之选择器

    一.前言 前面已经学习了缓冲和通道,接着学习选择器. 二.选择器 2.1 选择器基础 选择器管理一个被注册的通道集合的信息和它们的就绪状态,通道和选择器一起被注册,并且选择器可更新通道的就绪状态,也可 ...

  5. 【NIO】Java NIO之缓冲

    一.前言 在笔者打算学习Netty框架时,发现很有必要先学习NIO,因此便有了本博文,首先介绍的是NIO中的缓冲. 二.缓冲 2.1 层次结构图 除了布尔类型外,其他基本类型都有相对应的缓冲区类,其继 ...

  6. 我的Java开发学习之旅------>Java NIO 报java.nio.charset.MalformedInputException: Input length = 1异常

    今天在使用Java NIO的Channel和Buffer进行文件操作时候,报了java.nio.charset.MalformedInputException: Input length = 1异常, ...

  7. 【JAVA NIO】java NIO

    本文是博主深入学习Netty前的一些铺垫,之前只是使用Netty,用的很粗暴,导包,上网找个DEMO就直接用,对Netty中的组件了解并不深入. 于是再此总结下基础,并对一些核心组件作如下记录: 1. ...

  8. 【NIO】Java NIO之通道

    一.前言 前面学习了缓冲区的相关知识点,接下来学习通道. 二.通道 2.1 层次结构图 对于通道的类层次结构如下图所示. 其中,Channel是所有类的父类,其定义了通道的基本操作.从 Channel ...

  9. Java入门系列Java NIO

    jdk1.4中新加入的NIO,引入了通道与缓冲区的IO方式,它可以调用Native方法直接分配堆外内存,这个堆外内存就是本机内存,不会影响到堆内存的大小.

  10. Java NIO 系列教程(转)

    原文中说了最重要的3个概念,Channel 通道Buffer 缓冲区Selector 选择器其中Channel对应以前的流,Buffer不是什么新东西,Selector是因为nio可以使用异步的非堵塞 ...

随机推荐

  1. Spring Boot 使用Mybatis注解开发增删改查

    使用逆向工程是遇到的错误 错误描述 org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): c ...

  2. linux软件操作

    操作 命令 ubuntu 源操作 源配置 https://www.cnblogs.com/wenlin-gk/p/11146228.html 源更新 sudo apt-get update 查看源中包 ...

  3. 1. svn 简介

    参考文档: http://svndoc.iusesvn.com/ SVN的 相关网站 什么是svn?Subversion是一个“集中式”的信息共享系统.版本库是Subversion的核心部分,是数据的 ...

  4. 使用AnnotationConfigApplicationContext注册配置类

    1. AnnotationConfigApplicationContext功能 该类可以实现基于Java的配置类加载自定义在Spring的应用上下文的bean. 1.1 使用方式一:在构造方法中完成注 ...

  5. 一些性能优化的tips

    工作中积累的一些性能优化的tips,记录一下: 1. Message的创建 Message message = Message.obtain();  // 推荐 Message message = n ...

  6. 基于BootStrap的分页代码实现

    public class PageUtil { //targetUrl 访问url totalNum总记录数 currentPage 当前页数 pageSize每页的大小 public static ...

  7. oracle各服务说明及cmd启动启动命令

    成功安装Oracle 11g后,共有7个服务,一.这七个服务的含义分别为:1. Oracle ORCL VSS Writer Service:Oracle卷映射拷贝写入服务,VSS(Volume Sh ...

  8. Oracle诊断:使用USER_SEGMENTS分配给表的物理空间大小

    假设我的SCHEMA的名字是abc, 需要知道在这个SCHEMA下的数据容量,可以通过下面的方式. 1.登录SCHEMA abc 2.使用USER_SEGMENTS查看SCHEMA abc数据容量 S ...

  9. WCF - Home

    https://www.tutorialspoint.com/wcf/index.htm WCF Tutorial WCF stands for Windows Communication Found ...

  10. canvas万花筒案例

    <!DOCTYPE html><html><head> <meta charset="UTF-8"> <title>Ti ...