部分转载自: Java NIO原理及实例
部分转载自: Java NIO
Java IO的各种流是阻塞的。这意味着当一个线程调用read()和write()时,该线程被阻塞,知道数据被读出或者数据完全写入。
Java NIO的非阻塞模式,是一个线程从某通道发送请求读取数据,但是只可以读取到目前有的数据,如无数据则什么都不会获取。而不是保持线程阻塞。一个线程请求写入一些数据到某通道,但不需要等待它完全写入。
通道
类似于流,但是可以异步读写数据,且通道可以是双向的。通道的数据总是先读到一个buffer或者从一个buffer写入,即通道与buffer进行数据交互。
通道类型:
① FileChannel: 从文件中读取数据,不能切换到非阻塞模式(特殊)
② DatagramChannel: 能通过UDP读取网络中的数据
③ SocketChannel: 能通过TCP读取网络中的数据
④ ServerSocketChannel: 可以监听新进来的TCP连接。对每一个新进来的连接都会创建一
个SocketChannel
缓存区Buffer
属性:
capacity: 缓存区的容量,一旦设定不可更改
position: 初始值为0,读或写时,每一步加1
limit: 写模式下,limit代表着最大写入的数据量,此时limit=capacity。读模式下,limit=buffer中实
际数据大小
方法:
allocate(); 分配一块缓存区 ByteBuffer.allocate(1024)
put(); 向缓存区写数据
final byte[] hb;
public ByteBuffer put(byte x) {
hb[ix(nextPutIndex())] = x;
return this;
}
final int nextPutIndex() { // package-private
if (position >= limit)
throw new BufferOverflowException();
return position++;
}
get(); 从缓存区读数据
public byte get() {
return hb[ix(nextGetIndex())];
}
final int nextGetIndex() { // package-private
if (position >= limit)
throw new BufferUnderflowException();
return position++;
}
flip(); 将缓存区从写模式切换到读模式,position置为0
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
clear(); 从读模式切换为写模式,不会清空数据,但后续写操作会覆盖已有数据
public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
compact(); 从读模式切换为写模式,未读数据会被复制到缓存区头部,后续写操作在已有数据后
新增
public ByteBuffer compact() {
System.arraycopy(hb, ix(position()), hb, ix(0), remaining());
position(remaining());
limit(capacity());
discardMark();
return this;
}
mark(); 对position做出标记,配合reset使用
public final Buffer mark() {
mark = position;
return this;
}
reset(); 将position置为标记值
public final Buffer reset() {
int m = mark;
if (m < 0)
throw new InvalidMarkException();
position = m;
return this;
}
选择器(多路复用)
相当于一个观察者,用来监听通道感兴趣的事件,一个选择器可以绑定多个通道;用于实现非阻
塞IO
通道向选择器注册时,需要指定感兴趣的事件,选择器支持以下事件:
① SelectionKey.OP_CONNECT 成功建立TCP连接
② SelectionKey.OP_ACCEPT 接受TCP连接
③ SelectionKey.OP_READ
④ SelectionKey.OP_WRITE
// 开启一个Selector
Selector selector = Selector.open();
SocketChannel channel = SocketChannel.open();
// 将通道设为非阻塞模式,默认阻塞
channel.configureBlocking(false);
// 注册
SelectionKey key =
channel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);