同步非阻塞io模式,拿烧开水来说,NIO的做法是叫一个线程不断的轮询每个水壶的状态,看看是否有水壶的状态发生了改变,从而进行下一步的操作。
Java NIO有三大组成部分:Buffer,Channel,Selector,通过事件驱动模式实现了什么时候有数据可读的问题。
Channel:相当于IO操作的载体,相当于一个硬件设备,一个文件,一个socket或是区别程序中的不同IO操作,如read,write。
channel类似流,但又有些不同:
既可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的。
通道可以异步地读写。
通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入。
Buffer:用于和NIO通道进行交互。如你所知,数据是从通道读入缓冲区,从缓冲区写入到通道中的。
缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存。
channel 和 buffer 之间的交互如下:
Selector:Selector(选择器)是Java NIO中能够检测一到多个NIO通道,通道将关心的事件注册到selector 上,selector能够知晓通道是否为这些事件诸如读写事件做好数据准备。这样,一个单独的线程可以管理多个channel,从而管理多个网络连接。
2.什么是Java BIO?
同步阻塞IO模式,数据的读取写入必须阻塞在一个线程内等待其完成。这里使用那个经典的烧开水例子,这里假设一个烧开水的场景,有一排水壶在烧开水,BIO的工作模式就是, 叫一个线程停留在一个水壶那,直到这个水壶烧开,才去处理下一个水壶。但是实际上线程在等待水壶烧开的时间段什么都没有做。不知道io操作中什么时候有数据可读,所以一直是阻塞的模式。
3.区别及应用
主要区别如下:
首先,线程是较为重量级的资源。bio当并发量大,而后端服务或客户端处理数据慢时就会产生产生大量线程处于等待中,即上述的阻塞,是非常严重的资源浪费。此外,线程的切换也会导致cpu资源的浪费,单机内存限制也无法过多的线程,只能单向以流的形式读取数据。
nio使用单线程或者只使用少量的多线程,多个连接共用一个线程,消耗的线程资源会大幅减小。并且当处于等待(没有事件)的时候线程资源可以释放出来处理别的请求,通过事件驱动模型当有accept/read/write等事件发生后通知(唤醒)主线程分配资源来处理相关事件。以buffer缓冲区的形式处理数据,处理更为方便。
nio server demo:
Selector selector = Selector.open();
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.socket().bind(new InetSocketAddress(port));
ssc.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
// select()阻塞,等待有事件发生唤醒
int selected = selector.select();
if (selected > 0) {
Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
while (selectedKeys.hasNext()) {
SelectionKey key = selectedKeys.next();
if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {
SocketChannel client = ((ServerSocketChannel) key.channel()).accept();
// 处理 accept 事件
//注册read事件
client.configureBlocking(false);
client.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(bufSize));
} else if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
// 处理 read 事件
//注册write事件
} else if ((key.readyOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE) {
// 处理 write 事件
}
selectedKeys.remove();
}
}
}
bio server demo
ServerSocket serverSocket;
try {
serverSocket = new ServerSocket(8000);
while (true){
Socket socket = serverSocket.accept();
new Thread(()->{
try (InputStream inputStream = socket.getInputStream(); OutputStream outputStream = socket.getOutputStream()) {
byte[] bytes =new byte[1024];
while (inputStream.read(bytes) != -1){
outputStream.write(bytes);
bytes = new byte[1024];
}
}catch (IOException e){
e.printStackTrace();
}
}).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}