zoukankan      html  css  js  c++  java
  • 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();
        }
    
  • 相关阅读:
    cross-domain
    【转】React、Vue访问NotFound
    Flutter环境配置
    antd遇见的坑
    npm源设置
    React中的生命周期函数
    【备忘】javascript原型、Function、eval、闭包、json处理、类、arguments不定
    ADB获取手机信息
    selenium操作
    操作execl
  • 原文地址:https://www.cnblogs.com/ylcc-zyq/p/12811678.html
Copyright © 2011-2022 走看看