Selector

1. Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件。这样,一个单独的线程可以管理多个channel,从而管理多个网络连接。

2. Selector的创建:Selector selector = Selector.open();

3. 向Selector注册通道,为了将Channel和Selector配合使用,必须将channel注册到selector上。通过SelectableChannel.register()方法来实现:

channel.configureBlocking(false);

SelectionKey key = channel.register(selector,Selectionkey.OP_READ);

与Selector一起使用时,Channel必须处于非阻塞模式下。这意味着不能将FileChannel与Selector一起使用,因为FileChannel不能切换到非阻塞模式。而套接字通道都可以。

注意register()方法的第二个参数。这是一个“interest集合”,意思是在通过Selector监听Channel时对什么事件感兴趣。可以监听四种不同类型的事件:

Connect     Accept     Read     Write

通道触发了一个事件意思是该事件已经就绪。所以,某个channel成功连接到另一个服务器称为“连接就绪”。一个server socket channel准备好接收新进入的连接称为“接收就绪”。

一个有数据可读的通道可以说是“读就绪”。等待写数据的通道可以说是“写就绪”。这四种事件用SelectionKey的四个常量来表示:

SelectionKey.OP_CONNECT SelectionKey.OP_ACCEPT SelectionKey.OP_READ SelectionKey.OP_WRITE

如果你对不止一种事件感兴趣,那么可以用“位或”操作符将常量连接起来,如下:int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;

4. SelectionKey:当向Selector注册Channel时,register()方法会返回一个SelectionKey对象。这个对象包含了一些你感兴趣的属性:

interest集合:interest集合是你所选择的感兴趣的事件集合。可以通过SelectionKey读写interest集合,像这样:

    int interestSet = selectionKey.interestOps();

    boolean isInterestedInAccept = (interestSet & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;

    boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT;

    boolean isInterestedInRead = interestSet & SelectionKey.OP_READ;

    boolean isInterestedInWrite = interestSet & SelectionKey.OP_WRITE;

可以看到,用“位与”操作interest 集合和给定的SelectionKey常量,可以确定某个确定的事件是否在interest 集合中。

ready集合:ready 集合是通道已经准备就绪的操作的集合。在一次选择(Selection)之后,你会首先访问这个ready set。可以这样访问ready集合:

  int readySet = selectionKey.readyOps();

  可以用像检测interest集合那样的方法,来检测channel中什么事件或操作已经就绪。但是,也可以使用以下四个方法,它们都会返回一个布尔类型:

       selectionKey.isAcceptable();

       selectionKey.isConnectable();

       selectionKey.isReadable();

       selectionKey.isWritable();

Channel+ Selector:从SelectionKey访问Channel和Selector很简单。如下:

     Channel channel = selectionKey.channel();

     Selector selector = selectionKey.selector();

附加的对象(可选):可以将一个对象或者更多信息附着到SelectionKey上,这样就能方便的识别某个给定的通道。例如,可以附加 与通道一起使用的Buffer,或是包含聚集数据的某个对象。

使用方法如下:

selectionKey.attach(theObject);

Object attachedObj = selectionKey.attachment();

还可以在用register()方法向Selector注册Channel的时候附加对象。如:

SelectionKey key = channel.register(selector, SelectionKey.OP_READ, theObject);

5. 通过Selector选择通道:一旦向Selector注册了一或多个通道,就可以调用几个重载的select()方法。这些方法返回你所感兴趣的事件(如连接、接受、读或写)已经准备就绪的那些通道。

1). 下面是select()方法:

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

select(long timeout)和select()一样,除了最长会阻塞timeout毫秒(参数)。

selectNow()不会阻塞,不管什么通道就绪都立刻返回

select()方法返回的int值表示有多少通道已经就绪。亦即,自上次调用select()方法后有多少通道变成就绪状态。如果调用select()方法,因为有一个通道变成就绪状态,返回了1,

若再次调用select()方法,如果另一个通道就绪了,它会再次返回1。如果对第一个就绪的channel没有做任何操作,现在就有两个就绪的通道,但在每次select()方法调用之间,只有

一个通道就绪了。

2). 一旦调用了select()方法,并且返回值表明有一个或更多个通道就绪了,然后可以通过调用selector的selectedKeys()方法,访问“已选择键集(selected key set)”中的就绪通道。

如下所示:

Set selectedKeys = selector.selectedKeys();

当像Selector注册Channel时,Channel.register()方法会返回一个SelectionKey 对象。这个对象代表了注册到该Selector的通道。可以通过SelectionKey的selectedKeySet()方法访问

这些对象。可以遍历这个已选择的键集合来访问就绪的通道。如下:

 Set selectedKeys = selector.selectedKeys();

         Iterator keyIterator = selectedKeys.iterator();

         while(keyIterator.hasNext()) {

              SelectionKey key = keyIterator.next();

              if(key.isAcceptable()) {

                     // a connection was accepted by a ServerSocketChannel.

              } else if (key.isConnectable()) {

                     // a connection was established with a remote server.

              } else if (key.isReadable()) {

                     // a channel is ready for reading

              } else if (key.isWritable()) {

                    // a channel is ready for writing

              }

              keyIterator.remove();

          }

这个循环遍历已选择键集中的每个键,并检测各个键所对应的通道的就绪事件。

3). 注意每次迭代末尾的keyIterator.remove()调用。Selector不会自己从已选择键集中移除SelectionKey实例。必须在处理完通道时自己移除。下次该通道变成就绪时,Selector会

再次将其放入已选择键集中。SelectionKey.channel()方法返回的通道需要转型成你要处理的类型,如ServerSocketChannel或SocketChannel等。

6. wakeUp()

某个线程调用select()方法后阻塞了,即使没有通道已经就绪,也有办法让其从select()方法返回。只要让其它线程在第一个线程调用select()方法的那个对象上调用Selector.wakeup()

方法即可。阻塞在select()方法上的线程会立马返回。如果有其它线程调用了wakeup()方法,但当前没有线程阻塞在select()方法上,下个调用select()方法的线程会立即“醒来(wake up)”。

7. close():用完Selector后调用其close()方法会关闭该Selector,且使注册到该Selector上的所有SelectionKey实例无效。通道本身并不会关闭。

8. 完整的示例: http://weixiaolu.iteye.com/blog/1479656

FileChannel:

1. Java NIO中的FileChannel是一个连接到文件的通道。可以通过文件通道读写文件。FileChannel无法设置为非阻塞模式,它总是运行在阻塞模式下。

2. 打开FileChannel :

在使用FileChannel之前,必须先打开它。但是,我们无法直接打开一个FileChannel,需要通过使用一个InputStream、OutputStream或RandomAccessFile来获取一个FileChannel实例。

    RandomAccessFile aFile = new RandomAccessFile("data/nio-data.txt", "rw");

    FileChannel inChannel = aFile.getChannel();

3. 从FileChannel读取数据 : 调用多个read()方法之一从FileChannel中读取数据。如:

    ByteBuffer buf = ByteBuffer.allocate(48);

    int bytesRead = inChannel.read(buf);

read()方法返回的int值表示了有多少字节被读到了Buffer中。如果返回-1,表示到了文件末尾。

4. 向FileChannel写数据 : 使用FileChannel.write()方法向FileChannel写数据,该方法的参数是一个Buffer。

    while(buf.hasRemaining()) {
channel.write(buf);
}

注意FileChannel.write()是在while循环中调用的。因为无法保证write()方法一次能向FileChannel写入多少字节,因此需要重复调用write()方法,直到Buffer中已经没有尚未写入通道的字节。

5. 关闭FileChannel : 用完FileChannel后必须将其关闭:channel.close();

6. FileChannel的position方法:有时可能需要在FileChannel的某个特定位置进行数据的读/写操作。可以通过调用position()方法获取FileChannel的当前位置。也可以通过调用

    position(long pos)方法设置FileChannel的当前位置。如果将位置设置在文件结束符之后,然后试图从文件通道中读取数据,读方法将返回-1 —— 文件结束标志。如果将位置设置在

文件结束符之后,然后向通道中写数据,文件将撑大到当前位置并写入数据。这可能导致“文件空洞”,磁盘上物理文件中写入的数据间有空隙。

7. FileChannel的size方法:FileChannel实例的size()方法将返回该实例所关联文件的大小。

8. FileChannel的truncate方法:可以使用FileChannel.truncate()方法截取一个文件。截取文件时,文件将中指定长度后面的部分将被删除。如:channel.truncate(1024);这个例子截取文件的

前1024个字节。

9. FileChannel的force方法:FileChannel.force()方法将通道里尚未写入磁盘的数据强制写到磁盘上。出于性能方面的考虑,操作系统会将数据缓存在内存中,所以无法保证写入到FileChannel

    里的数据一定会即时写到磁盘上。要保证这一点,需要调用force()方法。force()方法有一个boolean类型的参数,指明是否同时将文件元数据(权限信息等)写到磁盘上。

下面的例子同时将文件数据和元数据强制写到磁盘上:

 channel.force(true);

NIO复习02的更多相关文章

  1. Linux NIO 系列(02) 阻塞式 IO

    目录 一.环境准备 1.1 代码演示 二.Socket 是什么 2.1 socket 套接字 2.2 套接字描述符 2.3 文件描述符和文件指针的区别 三.基本的 SOCKET 接口函数 3.1 so ...

  2. JAVA NIO复习笔记

    1. JAVA NIO是什么? 从JDK1.4开始,java提供了一系列改进的输入/输出处理的新功能,这些功能被统称为新IO(New IO,简称NIO),新增了许多用于处理输入/输出的类,这些类都被放 ...

  3. NIO复习03

    SocketChannel: 1. Java NIO中的SocketChannel是一个连接到TCP网络套接字的通道.可以通过以下2种方式创建SocketChannel: 打开一个SocketChan ...

  4. NIO复习01

    NIO 概述: 1. Java NIO 由以下几个核心部分组成:Channels       Buffers           Selectors 2. 主要Channel的实现:FileChann ...

  5. nodejs复习02

    process 这个模块是单线程的,无法完全利用多核CPU 基本信息 //程序目录 process.cwd(); //应用程序当前目录 process.chdir('/home'); //改变应用程序 ...

  6. C#基础总复习02

    继续更新第二篇: 1:一元运算符:++ -- ++:不管是前加加还是后加加,变量的值最终都会自身加一. 前加加和后加加的区别体现在参与运算的时候,如果是后加加,则首先拿原值参与运算, 运算完成后再自身 ...

  7. nio复习总结

    观察者: 多个对象依赖一个对象的状态, 当这个对象状态发生改变时,依次通知多个对象. 消息的分发和处理 事件驱动 / IO多路复用 借助select  epoll等 reactor: io事件触发时, ...

  8. 防火墙和SELinux复习02

    1.防火墙 防火墙主要起隔离作用,严格的过滤入站,允许出站.又分为硬件防火墙和软件防火墙,硬件防火墙主要保护一群机器,而软件防火墙主要保护本机. 防火墙相关命令:systemctl status fi ...

  9. java数据结构复习02

    1.递归问题 1.1汉诺塔问题(递归) 问题描述三个柱子,起初有若干个按大小关系顺序安放的盘子,需要全部移动到另外一个柱子上.移动规则:在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘.移动 ...

随机推荐

  1. 从以下哪一个选项中可以获得Servlet的初始化参数。

    从以下哪一个选项中可以获得Servlet的初始化参数. A.Servlet B.ServletContext C.ServletConfig D.GenericServlet 解答:C servlet ...

  2. docker-py环境配置

    一.系统环境版本介绍: os-version: Linux -.el7.x86_64 python-version: Python six-version: python-six--.el7.noar ...

  3. asp.net 读取配置文件方法

    方法1: System.Collections.Specialized.NameValueCollection nvc = (System.Collections.Specialized.NameVa ...

  4. poj 3680(最小费用最大流)

    题目链接:http://poj.org/problem?id=3680 思路:因为N<=200,而区间范围为[1,100000],因此需要离散化,去重,然后就是建图了相连两点连边,容量为k,费用 ...

  5. SurvivalShooter学习笔记(三.敌人移动)

    1.敌人和玩家若存活,敌人始终朝着玩家所在位置移动,所以要给玩家物体一个Tag:Player从而找到玩家 2.敌人的自动寻路使用Unity自带的NavMeshAgent寻路组件寻路,要先把场景中不动的 ...

  6. xml html xhtml html5

    1.XML 什么是 XML? XML 指可扩展标记语言(EXtensible Markup Language) XML 是一种标记语言,很类似 HTML XML 的设计宗旨是传输数据,而非显示数据 X ...

  7. Appcompat实现Action Bar的兼容性处理

    Appcompat实现Action Bar时,如果使用到split action bar或者Navigating Up with the App Icon需要考虑兼容性.下面介绍下split acti ...

  8. SQL系统函数——系统信息

    1.查看信息1.1.查看编号和名称select @@SERVERNAME--SQL SERVER服务器的连接字符串,如:computername\instancenameselect @@SERVIC ...

  9. 【BZOJ4108】[Wf2015]Catering 有上下界费用流

    [BZOJ4108][Wf2015]Catering Description 有一家装备出租公司收到了按照时间顺序排列的n个请求. 这家公司有k个搬运工.每个搬运工可以搬着一套装备按时间顺序去满足一些 ...

  10. CodeForces 667A Pouring Rain

    A. Pouring Rain time limit per test 1 second memory limit per test 256 megabytes input standard inpu ...