Java专题十一:NIO
Posted 黄先生的日志
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java专题十一:NIO相关的知识,希望对你有一定的参考价值。
Java专题十一(2):NIO
0. NIO是什么
Non-blocking IO(非阻塞IO)
NIO可以让你非阻塞的使用IO,当线程从通道读取数据到缓冲区时,线程还是可以进行其他事情。当数据被写入到缓冲区时,线程可以继续处理它。从缓冲区写入通道也类似。
1. 通道Channel
通道代表面向实体的开放式连接,执行读写等IO操作,其中实体包括硬件设备,文件,网络套接字等
通道Channel
与缓冲区Buffer
密不可分,不同于IO从字符或字节流中读写操作,Channel
既可以从Buffer
中读取数据,也可以写入数据到Buffer
中
类图关系如下:
- Channel
- InterruptibleChannel
- FileChannel
- SelectableChannel:能与选择器
Selector
结合实现多路IO复用- SocketChannel
- ServerSocketChannel
- DatagramChannel
- ReadableByteChannel:读取Channel数据到Buffer中,
int read(ByteBuffer dst)
- WritableByteChannel:写入Buffer数据到Channel中,
int write(ByteBuffer src)
- InterruptibleChannel
Channel
中一些重要方法:
方法 | 说明 |
---|---|
open() |
用于创建Channel对象,如ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); |
SelectionKey register(Selector sel, int ops) |
只适用于SelectableChannel 类型的通道,用于向Selector中注册操作,当通道中有注册过的操作时,可以对通道进行操作。ops包括4种: SelectionKey.OP_ACCEPT /SelectionKey.OP_CONNECT /SelectionKey.OP_READ /SelectionKey.OP_WRITE |
SelectableChannel configureBlocking(boolean block) |
只适用于SelectableChannel 类型的通道,用于配置是否阻塞模式,如serverSocketChannel.configureBlocking(false); |
int read(ByteBuffer dst) |
将Channel里的数据读取到Buffer中 |
int write(ByteBuffer src) |
将Buffer中的数据写入到Channel中 |
2. 缓冲区Buffer
相当于内存块,可读可写
类图关系如下:
- Buffer
- ByteBuffer:
- HeapByteBuffer
- CharBuffer:
- ShortBuffer:
- IntBuffer:
- LongBuffer:
- FloatBuffer:
- DoubleBuffer:
- ByteBuffer:
- DirectBuffer
- DirectByteBuffer
ByteBuffer
中一些重要方法:
方法 | 说明 |
---|---|
allocate(int capacity) |
用于创建容量为capacity的Buffer对象,如ByteBuffer buffer = ByteBuffer.allocate(1024); |
boolean hasRemaining() |
是否有元素在position 和limit 之间 |
ByteBuffer put(byte b) |
将一个字节写入Buffer中 |
ByteBuffer put(byte[] src) |
将字节数组写入Buffer中 |
byte get() |
从Buffer中获取一个字节 |
byte get(byte[] dst) |
将Buffer中数据写入字节数组中 |
ByteBuffer flip() |
从写模式切换为读模式,limit = positon; positon = 0; mark = -1 |
ByteBuffer rewind() |
position = 0; mark = -1; |
ByteBuffer clear() |
position = 0; limit = capacity; mark = -1; |
ByteBuffer reset() |
position = mark; |
capacity
、position
、limit
说明:
- capacity:缓冲区的大小,最多可以存放capacity个数据
- position:写模式下,代表写入了多少个数据,使用
flip()
切换为读模式时,position设为0 - limit:读模式下,代表能读取到的数据大小,使用
flip()
切换为读模式时,limit设为position
3. 选择器Selector
一个
Selector
可以处理多个Channel
,通常与SelectableChannel
类型的Channel
结合使用
Selector
中一些重要方法:
方法 | 说明 |
---|---|
Selector open() |
用于创建一个Selector对象,如Selector selector = Selector.open(); |
int select() |
选择ready状态的操作集(keys set) |
Set<SelectionKey> selectedKeys() |
获取选择器已经选择的操作集(keys set) |
4.一个完整示例
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;
import java.util.Set;
public class Server {
public static void main(String[] args) throws Exception{
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress("127.0.0.1", 8017));
Selector selector = Selector.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while(true){
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while(iterator.hasNext()){
SelectionKey key = iterator.next();
if(key.isAcceptable()){
System.out.println("key.isAcceptable()");
iterator.remove();
SocketChannel socketChannel = ((ServerSocketChannel)key.channel()).accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
}else if(key.isReadable()){
System.out.println("key.isReadable()");
iterator.remove();
SocketChannel socketChannel = (SocketChannel)key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
socketChannel.read(buffer);
System.out.println(new String(buffer.array()));
socketChannel.register(selector, SelectionKey.OP_WRITE);
}else if(key.isWritable()){
System.out.println("key.isWritable()");
iterator.remove();
SocketChannel socketChannel = (SocketChannel)key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put(new String("i am server!!!").getBytes());
socketChannel.write(buffer);
System.out.println(new String(buffer.array()));
socketChannel.register(selector, SelectionKey.OP_READ);
}
}
}
}
}
- 运行Server.java
- 在CMD命令行输入
telnet 127.0.0.1 8017
,换行,输入ab - 控制台输出如下:
key.isAcceptable()
key.isReadable()
a
key.isWritable()
i am server!!!
key.isReadable()
b
key.isWritable()
i am server!!!
以上是关于Java专题十一:NIO的主要内容,如果未能解决你的问题,请参考以下文章