共计 4210 个字符,预计需要花费 11 分钟才能阅读完成。
NIO
java 1.4 版本引入, 给予缓冲区面 向通道的 io 操作
bio | nio |
---|---|
面向流 | 面向缓冲区(buffer) |
阻塞 io | 非阻塞 io |
同步 | 同步 |
无 | Selector(选择器) |
缓冲区: 是一个特定数据类型的容器, 有 java.nio 包定义, 所有的缓冲区都是 Buffer 抽象类的子类
子类:ByteBuffer CharBuffer ShortBuffer IntBuffer LongBuffer FloatBuffer DoubleBuffer
Buffer 次要用于和 NIO 通道进行通信, 数据从通道读入到缓冲区, 再从缓冲区读取到通道
Buffer 就像是一个数据能够保留多个类型雷同的数据
根本属性:
1. 容量(capacity): 示意缓冲区的最大容量 一旦创立不能批改
2. 限度(limit): 第一个不可读的索引, 即位于 limit 前面的数据不可读
3. 地位(position): 下一个要读取或写入数据的索引
4.flip: 将此时的 position 设为 limit,position 置为 0 - 个别是从 inputChannel 将数据读入到 buffer 而后将 buffer flip 后 为了从 buffer 中读取数据到 outputChannel
5. 标记 (mark) 和复原 (reset): 标记是一个索引, 通过 Buffer.mark() 指定一个特定的地位, 应用 reset 办法能够复原到这个地位
- 间接缓冲区: 程序间接操作物理映射文件
- 非间接缓冲区:jvm – 操作系统 – 物理内存
Channel: 相似于流, 然而 Channel 不能间接拜访数据, 只能与缓冲区进行交互通道
主体实现类
1.FileChannel: 用于读取 写入 映射和操作文件的通道
2.DataGramChannel: 通过 UDP 读取网络中的数据通道
3.SocketChannel: 通过 Tcp 读写通道的数据
4.ServerSocketChannel: 能够监听新进入的 Tcp 连贯, 对每一个新连贯创立一个 SocketChannel
提供 getChannel()办法的类
1.FileInputStream 2.FileOutputStream 3.RandomAccessFile 4.Socket 5.ServerSocket 6.DataGramSocket
通道工夫传输
1.transferFrom() 2.transferTo()
- 扩散读取 (Scatter): 将一个
Channel
中的数据扩散贮存到多个Buffer
中 - 汇集写入 (Gather): 将多个
Buffer
中的数据写入同一个Channel
中
Selector 个别被称为选择器, 也被称为多路复用器. 用于查看一个或多个通道是否处于可读可写如此能够实现一个线程治理多个 Channel
应用 Selector 带来的益处有: 应用更少的线程来解决 Channel, 能够避免上下文切换带来的性能小号
能够被抉择 (多路复用) 的 Channel 都继承自 SelectableChannel
SelectableChannel
||
AbstractSelectableChannel
|| || ||
DataGramChannel SocketChannel ServerSocketChannel
所以 FileChannel 不适应与 Selector, 即不能切换为非阻塞模式
Selector 应用根本步骤
1. 创立 Selector: Selector selector = Selector.open();
2. 设置为非阻塞为:channel.configureBlocking(false);
3. 注册 Channel 到 Selector:
/**
* 参数 -1: 要注册到的多路复用器
* 参数 -2: 是一个 "interest 汇合", 即要监听事件的汇合(有以下四种)
* OP_CONNECT 连贯
* OP_ACEEPT 接管
* OP_READ 读
* OP_WRITE 写
*/
SelectionKey key = channel.register(selector,SelectionKey.OP_READ);
如果要监听多种事件如下:
SelectionKey key = channel.register(selector,SelectionKey.OP_CONNECT | SelectionKey.OP_READ);
4. 而后就 连贯就绪 | 接管就绪 | 读就绪 | 写就绪
Selector 次要办法
办法 | 形容 |
---|---|
Set<SelectKey> keys() |
返回所有 SelectionKey 汇合, 代表 注册在这个 Selector 上 的Channel |
Set<SelectKey> selectedKeys() |
返回已抉择了的(即有 io 操作的)SelectionKey |
int select() |
监控所有注册了的 Channel , 如果有须要 io 的操作时会将对应的selectKey 退出到 selectedKeys 汇合中, 返回的则是被抉择 (有 io 操作的)Channel 数量, 这个操作时阻 塞的即只有被抉择的 Channel 数量 >= 1 才 返回 |
int select(timeout) |
有超时时长, 始终没有 io 操作的 Channel 呈现, 达到 timeout 呈现的工夫后将主动返回 |
int selectNow() |
无阻塞 立刻返回 |
Selector wakeUp() |
使正在 select() 立刻返回 |
void close() |
敞开 |
SelectionKey 次要办法
SelectionKey
示意 Channel
和Selector
之间的关系,Channel
向 Selector
注册就会产生一个SelectionKey
办法 | 形容 |
---|---|
int interestOps() |
感兴趣事件的汇合 boolean isInterested = interestSet & SelectionKey.OP_CONNECT … |
int readyOps() |
获取通道筹备好就绪的操作 |
SelectableChannel channel() |
获取注册通道 |
Selector selector() |
获取选择器 |
boolean isConnectable() |
检测 Channel 中是否有连贯事件就绪 |
boolean isAcceptable() |
检测 Channel 中是否有接管事件就绪 |
boolean isReadaable() |
检测 Channel 中是否有读事件就绪 |
boolean isWriteable() |
检测 Channel 中是否有写事件就绪 |
Object attach() |
将一个对象附着到 SelectionKey 上, 次要是一些用于标识的信息 |
Object attachment() |
获取注册信息 也能够在 Channel 注册的时候附着信息 SelectionKey key = channel.register(selector, SelectionKey.OP_READ, theObject); |
void cancel() |
申请勾销此键的通道到其选择器的注册 |
package com.yuan.nio.selector;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class NioServer {public static void main(String[] args) throws IOException {
// 创立服务端通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 非阻塞模式
serverSocketChannel.configureBlocking(false);
// 绑定端口
serverSocketChannel.bind(new InetSocketAddress(9021));
// 创立选择器
Selector selector = Selector.open();
// 注册 接管
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
// 有一个事件时就操作
while (selector.select() > 0) {
// 获取事件汇合
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {SelectionKey selectionKey = iterator.next();
// 如果是接管就绪
if (selectionKey.isAcceptable()) {
// 获取客户端连贯
SocketChannel socketChannel = serverSocketChannel.accept();
// 切换成非阻塞
socketChannel.configureBlocking(false);
// 注册在多路复用器上 读
socketChannel.register(selector, SelectionKey.OP_READ);
// 读事件
} else if (selectionKey.isReadable()) {
// 获取客户端连贯
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
// 设置缓存
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int len = 0;
while (-1 != (len = socketChannel.read(byteBuffer))) {byteBuffer.flip();
System.out.println(new String(byteBuffer.array(),0,len));
byteBuffer.clear();}
// 申请勾销此键的通道在其选择器的注册, 也就是 selector.select(); 的数量 -1 selectionKey.cancel();
socketChannel.close();}
}
iterator.remove();}
}
}
正文完