Java非阻塞IO NIO之Selector选择器

    xiaoxiao2025-02-12  17

    NIO自从JDK1.4版本以来就添加的一个非阻塞I/O框架,NIO是Java为解决网络通讯中高并发问题的一个类库,Selector是java NIO的一个组件,用于检查一个或多个NIO Channel的状态是否处于可读、可写。如此可以实现单线程管理多个channels,也就是可以管理多个网络链接,所以Selecotr是实现了多路复用的关键。

    为什么要使用Selector呢?

    用单线程处理多个channels的好处是我需要更少的线程来处理channel。实际上,你甚至可以用一个线程来处理所有的channels。从操作系统的角度来看,切换线程开销是比较昂贵的,并且每个线程都需要占用系统资源,因此使用的线程自然是越少越好。

    需要留意的是,现代操作系统和CPU在多任务处理上已经变得越来越好,所以多线程带来的影响也越来越小。如果一个CPU是多核的,如果不执行多任务反而是浪费了机器的性能。不过这些设计讨论是另外的话题了。简而言之,通过Selector我们可以实现单线程操作多个channel。

    这有一幅示意图,描述了单线程处理三个channel的情况:

     

    图一

    从图中我们可以看到,一个线程通过使用Selector可以同时管理3个Channel,那么如何实现这种管理的呢?我们先来看看Selector的定义:选择器(Selector)是SelectableChannle 对象的多路复用器,Selector 可以同时监控多个SelectableChannel 的IO 状况。不同于BIO(阻塞式I/O),Selector通过将channel注册到相应的集合中,在注册时使用一个SelectionKey来表示当前channel的状态,从而实现管理多个channel

     

    创建一个selector的方法:

    Selector selector=Selector.open();//创建一个Selector是通过Selector提供的open方法实现的。

    注册Channel到Selector上,使得Selector可以管理Channel

    channel.configureBlocking(false);  //将阻塞设置为非阻塞 SelectionKey key = channel.register(selector, SelectionKey.OP_READ); 

    第一句代码是将channel转换为非阻塞模式,这一句是必不可少的,第二句代码在的使用方法是将一个chanel注册到一个selector中,在编写代码是应该注意一下,这个时候我们能够发现第二个参数SelectionKey.OP_READ,这个表示了当前channel的状态,我们来看看SelectionKey提供的其他几种状态:

    1.Connect   //此时处于“连接就绪”状态

    2.Accept   //此时处于“可连接就绪”状态

    3.Read      //此时处于“读就绪”状态

    4.Write     //此时处于“写就绪”状态

    这四种状态是用来表示channel的,在SelctionKey中他们分别对应:

    1.SelectionKey.OP_CONNECT

    2.SelectionKey.OP_ACCEPT

    3.SelectionKey.OP_READ

    4.SelectionKey.OP_WRITE

    若注册时不止监听一个事件,则可以使用“位或”操作符连接。

    public static final int OP_READ = 1 << 0; public static final int OP_WRITE = 1 << 2; public static final int OP_CONNECT = 1 << 3; public static final int OP_ACCEPT = 1 << 4; 只写出8位,其余的位置不写出来,大家知道int有32位就行 0000 0001 向左移0位后 0000 0001 0000 0001 向左移2位后 0000 0100 0000 0001 向左移3位后 0000 1000 0000 0001 向左移4位后 0001 0000 然后在对应位上为1表示监听对应事件,所以有: OP_READ|OP_WRITE =0000 0001 | 0000 0100 结果为:0000 0101,在第6位和第8位上为1,表示监听两种事件 注:这种方式也可以用来实现权限管理哦!

    channel.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE);

     

    图二

    看完了SelectionKey对象,我们再回过头来看看我们的Selector类,类中一个方法keys(),该方法返回的是一个SelectionKey类型的Set集合,该方法能够将所有状态为OP_CONNECT的SelectionKey集合,也就是返回所有接入Selector的channel,我们可以通过SelectionKey中的channel方法获得到Channel对象,这样的话,我们就获得了Seelectoe管理的所有Channel对象。有了这个对象我们自然可以完成很多东西,关于这方面的应用我就不多叙述了。

    Selector提供了一个select()方法,该方法返回所有可用的channel数量。

    Selector还提供了selectedKeys()方法,用于获得所有可用的channel。

    关于其他方法大家有兴趣可以看JDK的源码进行深一步的了解。

    参考文章:http://tutorials.jenkov.com/java-nio/selectors.html

    最新回复(0)