NIO
Java nio(new io)是从Java1.4版本开始引入的一个新的io api,可以替代标准的Java IO API。NIO与原来IO有同样的作用和目的。
- 面向缓存区(Buffer)
- 基于IO通道(Channel)
- 支持同步和异步
- 更高效读写
NIO和IO的主要区别
| IO | NIO | | :—-: | :—-: | | 面向流 | 面向缓存区 | | 同步IO | 同步IO/异步IO | | | 选择器(Selectors) |
Buffer的常用方法
方法 | 描述 |
---|---|
Buffer clear() | 清空缓冲区并返回对缓冲区的引用 |
Buffer flip() | 将缓冲区的界限设置为当前位置,并将当前位置充值为 0 |
int capacity() | 返回 Buffer 的 capacity 大小 |
boolean hasRemaining() | 判断缓冲区中是否还有元素 |
int limit() | 返回 Buffer 的界限(limit) 的位置 |
Buffer limit(int n) | 将设置缓冲区界限为 n, 并返回一个具有新 limit 的缓冲区对象 |
Buffer mark() | 对缓冲区设置标记 |
int position() | 返回缓冲区的当前位置 position |
Buffer position(int n) | 将设置缓冲区的当前位置为 n , 并返回修改后的 Buffer 对象 |
int remaining() | 返回 position 和 limit 之间的元素个数 |
Buffer reset() | 将位置 position 转到以前设置的 mark 所在的位置 |
Buffer rewind() | 将位置设为为 0, 取消设置的 mark |
直接与非直接缓冲区
非直接缓存区
直接缓存区
通道(Channel)
Java 为Channel接口提供的最主要实现类如下:
- FileChannel:用于读取、写入、映射和操作文件的通道。
- DatagramChannel:通过UDP读写网络中的数据通道。
- SocketChannel:通过TCP读写网络中的数据。
- ServerSocketChannel:可以监听新进来的TCP连接,对每一个新进来 的连接都会创建一个SocketChannel。
获取通道:
- FileInputStream.getChannel()
- FileOutputStream.getChannel()
- RandomAccessFile.getChannel()
- DatagramSocket.getChannel()
- Socket.getChannel()
- ServerSocket.getChannel()
- XXXChannel.open()
transferFrom()和transferTo()
将数据从源通道传输到其他 Channel 中,该方法操控直接内存
选择器(Selector)
选择器(Selector)是SelectableChannle对象的多路复用器,Selector可以同时监控多个SelectableChannel的IO状况,也就是说,利用Selector可使一个单独的线程管理多个Channel。Selector是非阻塞IO的核心。
方法 | 描述 |
---|---|
Set keys() | 所有的SelectionKey集合。代表注册在该Selector上的Channel |
selectedKeys() | 被选择的SelectionKey集合。返回此Selector的已选择键集 |
int select() | 监控所有注册的Channel,当它们中间有需要处理的IO操作时,该方法返回,并将对应得的SelectionKey加入被选择的SelectionKey 集合中,该方法返回这些Channel的数量。 |
int select(long timeout) | 可以设置超时时长的select()操作 |
int selectNow() | 执行一个立即返回的select()操作,该方法不会阻塞线程 |
Selector wakeup() | 使一个还未返回的select()方法立即返回 |
void close() | 关闭该选择器 |
SelectionKey
表示 SelectableChannel 和Selector之间的注册关系。每次向 选择器注册通道时就会选择一个事件(选择键)。选择键包含两个表示为整 数值的操作集。操作集的每一位都表示该键的通道所支持的一类可选择操 作。
方法 | 描述 |
---|---|
int interestOps() | 获取感兴趣事件集合 |
int readyOps() | 获取通道已经准备就绪的操作的集合 |
SelectableChannel channel() | 获取注册通道 |
Selector selector() | 返回选择器 |
boolean isReadable() | 检测Channal中读事件是否就绪 |
boolean isWritable() | 检测Channal中写事件是否就绪 |
boolean isConnectable() | 检测Channel中连接是否就绪 |
boolean isAcceptable() | 检测Channel中接收是否就绪 |
代码
public class NioSocket {
@Test
public void sent() {
try (SocketChannel sc = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9098))) {
sc.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.allocate(1024);
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
String s = scanner.next();
buffer.put(s.getBytes());
buffer.flip();
sc.write(buffer);
buffer.clear();
}
} catch (IOException ignored) {
}
}
@Test
public void accept() {
try (ServerSocketChannel sc = ServerSocketChannel.open()) {
sc.bind(new InetSocketAddress(9098));
sc.configureBlocking(false);
ByteBuffer buffer = ByteBuffer.allocate(1024);
Selector selector = Selector.open();
sc.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 socket = sc.accept();
socket.configureBlocking(false);
socket.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) {
SocketChannel socket = (SocketChannel) selectionKey.channel();
while (socket.read(buffer) > 0) {
buffer.flip();
System.out.println(new String(buffer.array(),0,buffer.limit()));
buffer.clear();
}
}
iterator.remove();
}
}
} catch (IOException ignored) {
}
}
}
OpenOption实现类
StandardOpenOption | 解释 |
---|---|
READ | 以读取方式打开文件 |
WRITE | 已写入方式打开文件 |
CREATE | 如果文件不存在,创建 |
CREATE_NEW | 如果文件不存在,创建;若存在,异常 |
APPEND | 在文件的尾部追加 |
DELETE_ON_CLOSE | 当流关闭的时候删除文件 |
TRUNCATE_EXISTING | 把文件设置为0字节 |
SPARSE | 文件不够时创建新的文件 |
SYNC | 同步文件的内容和元数据信息随着底层存储设备 |
DSYNC | 同步文件的内容随着底层存储设备 |