`

Java NIO之Selector、SelectableChannel和SelectorProvider

    博客分类:
  • java
阅读更多
高性能IO模型浅析 http://www.cnblogs.com/fanzhidongyzby/p/4098546.html
系统间通讯方式之(Java NIO多路复用模式)(四) http://blog.csdn.net/u010963948/article/details/78507255
java NIO selector全面深入理解 http://blog.csdn.net/lw305080/article/details/51205545
 Java NIO之Selector、SelectableChannel和SelectorProvider  http://blog.csdn.net/zilong_zilong/article/details/79588449

 

1.Selector 

        Selector是一个多路复用器,它负责管理被注册到其上的SelectableChannel。Selector的实现根据操作系统的不同而不同,目前多路复用IO实现主要包括四种:select、poll、epoll、kqueue,这里借用http://blog.csdn.net/u010963948/article/details/78507255博客中4种多路复用I/O实现的总结,列表如下:

IO模型 相对性能 关键思路 操作系统 JAVA支持情况
select 较高 Reactor windows/Linux

支持,Reactor模式(反应器设计模式)。

Linux操作系统的 kernels 2.4内核版本之前,默认使用select;

而目前windows下对同步IO的支持,都是select模型

poll 较高 Reactor Linux

Linux下的JAVA NIO框架,

Linux kernels 2.6内核版本之前使用poll进行支持。

也是使用的Reactor模式

epoll Reactor/Proactor Linux

Linux kernels 2.6内核版本及以后使用epoll进行支持;

Linux kernels 2.6内核版本之前使用poll进行支持;

另外一定注意,由于Linux下没有Windows下的IOCP技术提供真正的 异步IO 支持,所以Linux下使用epoll模拟异步IO

kqueue Proactor Linux 目前JAVA的版本不支持

 

    1.1 Selector在Java里的默认实现

        Selector在Java里的默认实现如下图:

        默认JDK提供了对于POLL、KQUEUE这2个多路复用I/O模型的实现,对于其他实现则转交代理类SelectorProvider来提供,而SelectorProvider则根据不同操作系统的内核不同而不同,JDK对POLL、KQUEUE的实现类的组成绘图如下:

 

    1.2 Selector的创建、关闭

        Selector有2种创建方式

  • Selector.open():会调用操作系统底层的Selector实现来创建Selector;
  • SelectorProvider.openSelector():这种方式直接调用SelectorProvider类来获取操作系统底层的Selector来创建Selector。

        selector的关闭方法如下

  • Selector.close()

    1.3 Selector里维护的集合

        每个SelectableChannel会调用其SelectionKey register(Selector sel, int ops, Object att)方法向Selector注册,注册完毕后收到一个SelectionKey对象。Selector对象里维护着3个类型为java.util.Set的集合:

分类 描述
key集合 这个集合里都是注册到Selector的SelectableChannel对应的SelectionKey。要获得这些SelectionKey可以调用Selector.keys()。
selected-key集合 Selector会定期向操作系统内核询问是否有事件要通知到SelectableChannel,会将那些对这些事件感兴趣的SelectableChannel当时注册到Selector的SelectionKey复制一份到此集合,要获得这些SelectionKey集合可以调用Selector.selectedKeys(),这个集合是key集合的子集。
cancelled-key集合 这个集合里记录的是那些取消了对操作系统内核事件关注但并未取消对于Selector注册的SelectableChannel对应的SelectionKey的集合,这个集合不能直接访问,通常这个集合是key集合的子集。


 

        在SelectableChannel.register(Selector sel, int ops, Object att)调用后,会收到一个SelectionKey,并且SelectionKey已经在此方法调用后被添加到key集合,而cancelled-key集合中的SelectionKey会在Selector每次询问操作系统内核是否有事件通知时,从key集合中删除这些SelectionKey,而key集合自身不能直接被修改。

        在SelectionKey.cancel()或者SelectionKey对应的SelectableChannel.close()调用后,这个SelectionKey会被添加到cancelled-key集合,在下一个询问操作系统内核是否有事件通知时,会将这些取消了的SelectionKey从Selector对应的3个集合(key集合、selected-key集合、cancelled-key集合)中清理掉,同时这些SelectionKey对应的SelectableChannel也会从Selector中被注销。

        只有Selector.select()、Selector.select(long timeout)、Selector.selectNow()被调用并且操作系统内核是有事件通知才会将那些对这些事件感兴趣的SelectableChannel对应的SelectionKey放入selected-key集合,除此之外别无其它方法。只有直接调用selected-key集合的 Set.remove(Object o)或者通过Set.iterator()获取到的Iterator上调用Iterator.remove()来从selected-key集合删除某个SelectionKey,除此之外别其它方法。

 

    1.4 Selector的Selection操作

        Selector的Selection操作执行的3个步骤如下:

        (1)cancelled-key集合中的SelectionKey,首先会从Selector的key集合selected-key集合中被清除,接着cancelled-key集合中的SelectionKey对应的SelectableChannel会从Selector上进行注销,最后cancelled-key集合会被清空;

        (2)操作系统内核会被询问,从而获知哪些SelectionKey对应的SelectableChannel有感兴趣的事件通知发生,如果有SelectableChannel对此事件感兴趣,会执行如下2个步骤:

    • 如果该SelectableChannel对应的SelectionKey不在selected-key集合中,那么这个SelectableChannel对应的SelectionKey会被添加到selected-key集合中,同时该SelectableChannel对应的SelectionKey的成员变量readyOps会修改为本次事件对应的事件的值,而之前readyOps上记录的值会被覆盖;
    • 如果该SelectableChannel对应的SelectionKey在selected-key集合中,那么该SelectableChannel对应的SelectionKey的成员变量readyOps会修改为本次事件对应的事件的值,并且之前readyOps上记录的值会被保留,保留方式是将2个int进行或运算;

        如果key集合中的所有SelectionKey对应的SelectableChannel都对本次操作系统内核事件通知不感兴趣,那么Selector上的selected-key集合不会进行更新,并且SelectionKey的成员变量readyOps就不会被更新。

        (3)如果在步骤(2)中有SelectionKey被添加到selected-key集合,那么久按照步骤(1)的方式再次被处理。

 

    1.5 Selector的Concurrency控制

        Selector是线程安全的,可以被多个线程使用,但是Selector里的key集合、selected-key集合、cancelled-key集合不是线程安全的。

        Selector的Selector.select()、Selector.select(long timeout)、Selector.selectNow()操作按照Selector对象加锁、Selector里的key集合加锁、Selector里的selected-key集合加锁的先后顺序进行加锁来保证并发线程操作Selector的线程安全。同时也会在上面提到的步骤1和步骤3中对Selector里的cancelled-key集合进行加锁来保证并发线程操作Selector的线程安全。

        Selector的Selector.select()、Selector.select(long timeout)、Selector.selectNow()操作在被加锁处理期间,如果外界有新的SelectableChannel注册到Selector或者已有的SelectableChannel对应的SelectionKey上I/O事件发生变化,会在下一次Selector的Selector.select()、Selector.select(long timeout)、Selector.selectNow()操作执行时可见。

        SelectionKey.cancel()或者SelectableChannel.close()可能在任何时候调用,Selector不能保证其维护的的集合(key集合、selected-key集合、cancelled-key集合)中的SelectionKey是否有效和SelectionKey对应的SelectableChannel仍处于打开状态,因此应用程序要自己时刻使用同步机制来检查是否其它线程调用了SelectionKey.cancel()或者SelectableChannel.close()。

        线程在调用Selector的Selector.select()、Selector.select(long timeout)、Selector.selectNow()如果被阻塞,有如下3种方法中断:

    • Selector.wakeup():
    • Selector.close():
    • 阻塞线程的Thread.interrupt():

        Selector的key集合selected-key集合在多线程中使用是非线程安全的。如果想对这2个集合中的数据进行修改必须对集合进行加锁来保证同步。Selector的key集合selected-key集合的Set.iterator()获取到的Iterator容易fail-fast(fail-fast 机制是java集合(Collection)中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件),如果尝试调用Iterator.remove()会获得一个java.util.ConcurrentModificationException异常。 

 

 

2.SelectableChannel

        SelectableChannel是一个连接到操作系统的连接通道,用于应用程序和操作系统交互、传递数据。连接到操作系统上的每个SelectableChannel会有一个专属的文件状态描述符标识。SelectableChannel是双向的,可以读取数据,也可以写数据。

        JDK对于SelectableChannel的默认实现如下:

        SelectableChannel要和I/O多路复用选择器Selector一起使用,首先需要调用SelectableChannel的SelectionKey register(Selector sel, int ops, Object att)方法来向Selector注册,这个方法会返回一个SelectionKey对象,SelectionKey维护着与之关联的Selector和SelectableChannel的引用。用一张图来看下SelectionKey组成:

        SelectableChannel可以是阻塞的也可以是非阻塞的,阻塞方式下,在操作系统内核I/O事件触发时必须等SelectableChannel处理完上一个I/O事件后才能进行本次I/O事件处理。非阻塞方式下,操作系统内核I/O事件触发被SelectableChannel处理不会被阻塞,相对来说其传输的字节数也会很少。

        默认创建的SelectableChannel都是阻塞的,非阻塞模式更适合于在多路复用I/O下使用。非阻塞SelectableChannel必须在注册到Selector之前被设置为非阻塞,一旦注册完毕后,只能在取消注册到Selector后才能修改非阻塞为阻塞。

 

3.SelectorProvider

        不同操作系统的I/O多路复用选择器各自内核实现不同,目前有select、poll、epoll、kqueue四种实现,JDK里与之对应的实现也随着操作系统的不同而不同,所以ORACLE官网提供的JDK下载需要区分不同的版本,因为每个操作系统底层的实现各不相同。

         JDK里对于Selector的实现都交由SelectorProvider的方法publicstatic SelectorProvider provider()来提供,这个方法的代码注释如下:

public static SelectorProvider provider() {
        synchronized (lock) {
            //provider不为空,直接返回provider
            if (provider != null) return provider;
            //AccessController.doPrivileged属于特权操作,意思是不管此方法由哪个用户发起,都无需对此操作涉及的资源(文件读写特权等等)进行检查
            return AccessController.doPrivileged(
                new PrivilegedAction<SelectorProvider>() {
                    public SelectorProvider run() {
                            //由JDK的参数-Djava.nio.channels.spi.SelectorProvider=class设置的class来反射构造SelectorProvider
                            if (loadProviderFromProperty())
                                return provider;

                            //从jar中的目录META-INF/services配置文件中找参数java.nio.channels.spi.SelectorProvider=class设置的第一个class来反射构造SelectorProvider
                            if (loadProviderAsService())
                                return provider;

                            //调用不同操作系统版本的JDK里自带的sun.nio.ch.DefaultSelectorProvider来创建SelectorProvider
                            provider = sun.nio.ch.DefaultSelectorProvider.create();
                            return provider;
                        }
                    });
        }
    }
        上面我们看到SelectorProvider的创建分三步进行:

        (1)由JDK的参数-Djava.nio.channels.spi.SelectorProvider=class设置的class来反射构造SelectorProvider,找不到就跳转到步骤(2)

        (2)从jar中的目录META-INF/services配置文件中找参数java.nio.channels.spi.SelectorProvider=class设置的第一个class来反射构造SelectorProvider,找不到就跳转到步骤(3)       

        (3)调用不同操作系统版本的JDK里自带的sun.nio.ch.DefaultSelectorProvider来创建SelectorProvider

        一般都会走到最后的步骤(3),而这个步骤里创建的SelectorProvider在各个操作系统对应的JDK里各不相同。sun.nio.ch.DefaultSelectorProvider这个类最终编译后放置在JDK的安装根目录下的jre/lib/rt.jar里。

        windows版本JDK里sun.nio.ch.DefaultSelectorProvider源代码如下:

package sun.nio.ch;
import java.nio.channels.spi.SelectorProvider;
public class DefaultSelectorProvider
{
  public static SelectorProvider create()
  {
    return new WindowsSelectorProvider();
  }
}

        MAC版本JDK里sun.nio.ch.DefaultSelectorProvider源代码如下:

package sun.nio.ch;
import java.nio.channels.spi.SelectorProvider;
public class DefaultSelectorProvider
{
  public static SelectorProvider create()
  {
    return new KQueueSelectorProvider();
  }
}

        Linux版本JDK里sun.nio.ch.DefaultSelectorProvider源代码如下:

package sun.nio.ch;
import java.nio.channels.spi.SelectorProvider;
import java.security.AccessController;
import sun.security.action.GetPropertyAction;
public class DefaultSelectorProvider
{
  public static SelectorProvider create()
  {
    String str1 = (String)AccessController.doPrivileged(new GetPropertyAction("os.name"));

    if ("SunOS".equals(str1)) {
      return new DevPollSelectorProvider();
    }

    if ("Linux".equals(str1)) {
      String str2 = (String)AccessController.doPrivileged(new GetPropertyAction("os.version"));
      String[] arrayOfString = str2.split("\\.", 0);
      if (arrayOfString.length >= 2) {
        try {
          int i = Integer.parseInt(arrayOfString[0]);
          int j = Integer.parseInt(arrayOfString[1]);
          if ((i > 2) || ((i == 2) && (j >= 6))) {
            return new EPollSelectorProvider();
          }
        }
        catch (NumberFormatException localNumberFormatException)
        {
        }
      }
    }
    return new PollSelectorProvider();
  }
}

        不同操作系统里最终SelectorProvider的实现类如下:

操作系统下JDK JDK里SelectorProvider实现
windows JDK sun.nio.cn.WindowsSelectorProvider
MAC JDK sun.nio.ch.KQueueSelectorProvider
Linux JDK

1)os.name=SunOS时,sun.nio.ch.DevPollSelectorProvider

 

2)os.name=Linux并且os.version版本号中第一个版本大于2或者

第一个版本号等于2且第二个版本大于6时,

sun.nio.chEPollSelectorProvider

 

Linux kernels 2.6内核版本及以后使用epoll进行支持;

Linux kernels 2.6内核版本之前使用poll进行支持;

比如Linux version 3.10.0-327.el7.x86_64就是用的

sun.nio.chEPollSelectorProvider

 

  • 大小: 118.3 KB
  • 大小: 72.9 KB
  • 大小: 212.7 KB
  • 大小: 213.3 KB
  • 大小: 93.5 KB
分享到:
评论

相关推荐

    Java NIO——Selector机制解析三(源码分析)

    Java NIO,全称为Non-blocking Input/Output,是Java在1.4版本引入的一个新特性,旨在提供一种更高效、更灵活的I/O操作...结合实际项目需求,合理运用Java NIO的Selector机制,可以显著提升系统的吞吐量和响应速度。

    Java_NIO-Selector.rar_java nio_selector

    Selector是Java NIO框架中的核心组件,它使得单个线程能够管理多个通道(Channels),从而提高系统资源利用率并优化性能。下面我们将详细探讨Java NIO中的Selector机制。 1. **Selector的作用** Selector的主要功能...

    Java Nio selector例程

    java侧起server(NioUdpServer1.java),基于Java Nio的selector 阻塞等候,一个android app(NioUdpClient1文件夹)和一个java程序(UI.java)作为两个client分别向该server发数据,server收到后分别打印收到的消息...

    Java_NIO类库Selector机制解析.doc

    Java_NIO类库Selector机制解析.docJava_NIO类库Selector机制解析.docJava_NIO类库Selector机制解析.docJava_NIO类库Selector机制解析.doc

    JavaNIO库Selector机制解析.docx

    JavaNIO库Selector机制解析.docx

    java nio Selector的使用-客户端

    Selector是Java NIO中的核心组件之一,它允许单个线程处理多个通道(channels)的读写事件,极大地提高了服务器的并发能力。本篇文章将深入探讨如何在Java NIO中使用Selector处理客户端的I/O请求。 首先,我们需要...

    java NIO和java并发编程的书籍

    java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java NIO和java并发编程的书籍java...

    JavaNIO chm帮助文档

    Java NIO系列教程(六) Selector Java NIO系列教程(七) FileChannel Java NIO系列教程(八) SocketChannel Java NIO系列教程(九) ServerSocketChannel Java NIO系列教程(十) Java NIO DatagramChannel Java ...

    Java-NIO类库Selector机制解析.docx

    "Java NIO Selector 机制解析" ...了解Java NIO类库和Selector机制可以帮助程序员更好地使用Java NIO类库,提高I/O操作的性能。但需要注意到Java虚拟机对操作系统调用的影响,避免出现异常和错误。

    Java NIO实战开发多人聊天室

    11-Java NIO-Channel-分散和聚集.mp4 12-Java NIO-Buffer-概述.mp4 13-Java NIO-Buffer-基本使用.mp4 14-Java NIO-Buffer-三个属性和类型.mp4 17-Java NIO-Buffer-缓冲区分片.mp4 18-Java NIO-Buffer-只读缓冲区.mp4...

    Java-NIO之Selector.doc

    Java NIO(非阻塞I/O)中的Selector是一个核心组件,它允许单个线程处理多个通道(channels)上的I/O事件。Selector的角色就像一个交通指挥员,能够监控多个通道并决定哪个通道准备好进行读写操作,从而提高系统的...

    java NIO.zip

    Java NIO提供了FileChannel和FileLock,可以进行高效的文件读写和锁定操作。例如,可以使用FileChannel的transferTo()和transferFrom()方法在通道之间高效地传输数据,而无需临时创建缓冲区。 5. **多路复用器...

    Java NIO英文高清原版

    NIO在Java 1.4版本引入,提供了更高效的数据处理和通道通信方式,特别适用于高并发、大数据量的系统。Netty是一个基于NIO的高性能、异步事件驱动的网络应用框架,它简化了网络编程,广泛应用于服务器端应用开发。 ...

    java NIO实例

    `NIOServer.java`和`NIOClient.java`这两个文件很可能是用于演示Java NIO服务器端和客户端的基本操作。下面将详细介绍Java NIO的主要组件和工作原理,并结合这两个文件名推测它们可能包含的内容。 1. **Selector...

    深入了解java NIO之Selector(选择器)

    "深入了解 Java NIO 之 Selector(选择器)" Java NIO(Non-Blocking I/O)中,Selector(选择器)是一种重要的组件,它提供了选择执行已经就绪的任务的能力,使得多元 I/O 成为可能。在 Java 中,Selector 是一种...

    java NIO技巧及原理

    在Java NIO中,"新"主要体现在非阻塞和多路复用这两个特性上,这使得NIO更适合于高并发、低延迟的系统。 **Java IO原理:** Java IO基于流模型,分为输入流和输出流。流是一维数据序列,可以是从源到目标的单向流动...

    Java NIO Socket基本

    Java NIO(New Input/Output)是Java标准库中提供的一种I/O模型,与传统的 Blocking I/O(同步阻塞I/O)相对。NIO在Java 1.4版本引入,其设计...了解和掌握Java NIO对于提升Java程序员在服务器端编程的能力至关重要。

    一个java NIO的例子

    而Java NIO引入了选择器(Selector)和通道(Channel)的概念,允许单个线程同时处理多个连接,大大提高了系统在高并发环境下的性能。 本例子中的"NioServer"可能是一个简单的Java NIO服务器端程序,用于演示如何...

Global site tag (gtag.js) - Google Analytics