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)

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:
  • DirectBuffer
    • DirectByteBuffer

ByteBuffer中一些重要方法:

方法 说明
allocate(int capacity) 用于创建容量为capacity的Buffer对象,如ByteBuffer buffer = ByteBuffer.allocate(1024);
boolean hasRemaining() 是否有元素在positionlimit之间
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;

capacitypositionlimit说明:

  • 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);
                }
            }
        }
    }
}

  1. 运行Server.java
  2. 在CMD命令行输入telnet 127.0.0.1 8017 ,换行,输入ab
  3. 控制台输出如下:
key.isAcceptable()
key.isReadable()
a                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
key.isWritable()
i am server!!!                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
key.isReadable()
b                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
key.isWritable()
i am server!!!       

以上是关于Java专题十一:NIO的主要内容,如果未能解决你的问题,请参考以下文章

Java专题十一:IO

Java专栏

JVM 专题十三:运行时数据区直接内存

Java.nio:最简洁的递归目录删除

JVM专题-直接内存

Java学习的三个终极问题及学习路线规划,专题解析