NIO

通道和缓冲区

java NIO系统的核心在于:通道(Channel)和缓冲区(Buffer)。通道表示打开到IO设备(例如:文件、套接字)的连接。若需要使用NIO系统,需要获取用于连接IO设备的通道以及用于容纳数据的缓冲区,对数据进行处理。

简而言之,Channel负责传输,Buffer负责存储

缓冲区

在Java NIO中负责数据的存取,缓冲区就是数组。用于存取不同数据类型的数据。

根据数据类型不同(boolean以外),提供了相应类型的缓冲区:ByteBuffer 、CharBuffer、ShortBuffer、IntBuffer等。

上述缓冲区的管理方式几乎一致,通过allocate()获取缓冲区

缓冲区存取数据的两个核心方法

  • put():存入数据到缓冲区

  • get():从缓冲区取出数据

缓冲区中的四个核心属性

  • capacity:容量,表示缓冲区中最大存储数据的容量,一旦声明,不能改变
  • limit:界限,表示缓冲区中可以操作数据的大小。limit后数据不能进行读写
  • position:位置,表示缓冲区中正在操作数据的位置
  • mark:标记,表示记录当前position的位置。可以通过reset()恢复到mark位置

0<=mark<=position <= limit <= capacity

put()向缓冲中放入数据,position随之移动。

flip()切换为读 模式,limit移动到position的位置,position回到起点。

get(),position向后读取,直到limit。

rewind():可重复读,position回到起点,limit不变。

clear():清除缓冲区。但是缓冲区的数据依然存在,但是处于“被遗忘”状态

mark():记录当前position的位置

reset():恢复到mark的位置

直接缓冲区与非直接缓冲区

非直接缓冲区:通过allocate()方法分配缓冲区,将缓冲区建立在JVM的内存中

直接缓冲区:通过allocateDirect()方法分配直接缓冲区,将缓冲区建立在物理内存中,可以提高效率。

非直接缓存区是在用户地址空间进行建立缓冲区,在将用户地址空间的数据拷贝到内核地址空间,然后再读写物理磁盘。直接缓冲区则是直接在内存映射文件读写,不用再拷贝。

但是内存映射文件并不受JVM控制,并且创建和销毁开销较大。一般,最好仅在直接缓冲区在程序性能方面带来明显好处时分配它们。

通道(channel)

表示IO源于目标打开的连接。类似于流,不过channel本身不能直接访问数据,Channel只能与Buffer进行交互。在Java NIO中负责缓冲区中数据的传输。

通道的主要实现类

  • FileChannel
  • SocketChannel
  • ServerSocketChannel
  • DatagramChannel

获取通道

1.通过getChannel()方法

本地IO:

  • FileInputStream/FileOutputStream

  • RandomAccessFile

网络IO:

  • socket
  • ServerSocket
  • DatagramSocket

2.在JDK1.7中NIO.2 针对各个通道提供了静态方法open()

3.在JDK1.7中的NIO.2 的Files工具类的newByteChannel()

通道之间的数据传输

transferFrom()

transferTo()

也是通过直接缓冲区

分散(Scatter)与聚集(Gather)

分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中

聚集写入(Gathering Writes):将多个缓冲区的数据聚集到通道

就是使用缓冲区数组进行读写操作

阻塞与非阻塞

针对于网络通信。

	@Test
    public void client() throws IOException {
        //1.获取通道
        SocketChannel channel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));

        //2.配置非阻塞
        channel.configureBlocking(false);

        //3.分配指定大小的缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);

        //4.向服务器发送数据
        buf.put(LocalDateTime.now().toString().getBytes());
        buf.flip();
        channel.write(buf);

        //5.关闭通道
        channel.close();
    }

    @Test
    public void server() throws IOException {
        //1.获取通道
        ServerSocketChannel channel = ServerSocketChannel.open();
        //2.配置非阻塞
        channel.configureBlocking(false);
        //3.绑定连接
        channel.bind(new InetSocketAddress(9898));
        //4.获取选择器
        Selector selector = Selector.open();
        //5.将通道注册到选择器,并指定监听接收事件
        channel.register(selector, SelectionKey.OP_ACCEPT);

        while(selector.select()>0){
            Iterator<SelectionKey> it = selector.selectedKeys().iterator();
            while(it.hasNext()){
                SelectionKey key = it.next();
                //判断具体是什么事件就绪
                if(key.isAcceptable()){
                    //获取连接
                    SocketChannel client = channel.accept();
                    //非阻塞
                    client.configureBlocking(false);
                    client.register(selector,SelectionKey.OP_READ);
                }else if(key.isReadable()){
                    //读就绪,从通道中读取数据
                    SocketChannel channel1 = (SocketChannel) key.channel();
                    ByteBuffer buf = ByteBuffer.allocate(1024);
                    while(channel1.read(buf)!=-1){
                        buf.flip();
                        System.out.println(new String(buf.array(),0,buf.limit()));
                        buf.clear();
                    }
                }
                //取消选择键
                it.remove();
            }
        }

    }

管道

Java NIO管道是2个线程之间的单向数据连接。Pipe有一个soure通道和一个sink通道。数据会被写到sink通道,从source通道读取。

	@Test
    public void test() throws IOException {
        //获取管道
        Pipe pipe = Pipe.open();

        //分配缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);

        //写入数据
        Pipe.SinkChannel sink = pipe.sink();
        buf.put("向管道中存入数据".getBytes());
        buf.flip();
        sink.write(buf);

        buf.clear();
        //读取数据
        Pipe.SourceChannel source = pipe.source();
        source.read(buf);
        buf.flip();
        System.out.println(new String(buf.array(),0,buf.limit()));

        source.close();
        sink.close();
    }
原文地址:https://www.cnblogs.com/ylcc-zyq/p/12811678.html