JAVA NIO

作者: 虫儿飞ZLEI | 来源:发表于2019-04-21 00:10 被阅读0次

    也是IO操作,比如读写文件什么的,只是与传统IO操作方式不一样。

    image.png 传统IO

    缓冲区像火车一样,在通道中来回传输数据

    • 通道:表示打开到IO设备(比如文件)的连接
    • 缓冲区:用来容纳数据
      • ByteBuffer
      • CharBuffer
      • ShortBuffer
      • IntBuffer
      • LongBuffer
      • FloatBuffer
      • DoubleBuffer
    NIO

    2. 使用

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

    put(): 存入数据到缓冲区
    get(): 获取缓冲区的数据

    缓冲区中的四个核心属性

    缓冲区都继承Buffer
    • capacity:容量,缓冲区中最大存储数据的容量,一旦声明不能改变
    • limit:界限,缓冲区中可以操作数据的大小,limit限制缓冲区的部分数据可以读写,超过limit的部分,不能读写。
    • position:位置,缓冲区中正在操作的位置。position<=limit<=capacity
    • mark:标记,表示记录当前position的位置,可以通过reset()恢复到mark的位置。mark和reset组合使用,可以重复读取某一部分的数据,先mark记录position,然后读完了以后,在reset将position重置到之前mark的位置。
    image.png
        @org.junit.Test
        public void test1(){
            //1.分配指定大小的缓冲区
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            System.out.println(buffer.position());
            System.out.println(buffer.limit());
            System.out.println(buffer.capacity());
    
            buffer.put("111".getBytes());
            System.out.println(buffer.position());
            System.out.println(buffer.limit());
            System.out.println(buffer.capacity());
    
            //切换到读取模式,后,positon和limit的值都会 变化
            buffer.flip();
    
            System.out.println(buffer.position());
            System.out.println(buffer.limit());
            System.out.println(buffer.capacity());
    
            buffer.get();
            System.out.println(buffer.position());
            System.out.println(buffer.limit());
            System.out.println(buffer.capacity());
        }
    
    buf.rewind() :可重复读数据,相当于重置。就可以重新读取数据了
    
    buf.clear(): 清空缓冲去,但是缓冲区的数据依然存在,只是把position和limit这些重置成最开始的状态,但是以前的数据还在的。
    

    3 直接缓冲区和非直接缓冲区

    • 非直接缓冲区:通过allocate() 方法分配缓冲区,将缓冲区建立在JVM内存中
    • 直接缓冲区(只有ByteBuffer支持):通过allocateDirect()方法分配直接缓冲区,将缓冲区建立在操作系统的物理内存中。可以提高效率。
        @org.junit.Test
        public void test2(){
            //分配直接缓冲区
            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
            //判断当前是否是直接缓冲区
            System.out.println(byteBuffer.isDirect());
        }
    

    4 通道

    通道的主要实现类

    • FileChannel
    • SocketChannel
    • ServerSocketChannel
    • DatagramChannel

    获取通道三种方式;

    1. 针对支持通道的类提供的getChannel方法
    • 本地IO:
      FileInputStream/FileOutputStream
      RandomAccessFile

    • 网络IO:
      Socket
      ServerSocket
      DatagramSocket

    1. 针对各个通道提供了静态方法Open(),jdk
      1.7
      FileChannel.open()
    1. Files工具类的newByteChannel
      Files.newByteChannel()

    5. 复制文件示例

    复制文件示例(非直接缓冲区)

        @org.junit.Test
        public void test3(){
            //获取流
            FileInputStream fis = null;
            FileOutputStream fos = null;
    
            //获取通道
            FileChannel inChannel  = null;
            FileChannel outChannel = null;
    
            try {
                fis = new FileInputStream("D:\\WorkSpace\\WorkSpace\\Java_idea\\NIO\\nio\\src\\main\\resources\\1.jpg");
                fos = new FileOutputStream("D:\\WorkSpace\\WorkSpace\\Java_idea\\NIO\\nio\\src\\main\\resources\\2.jpg");
    
                inChannel = fis.getChannel();
                outChannel = fos.getChannel();
    
                //分配指定大小的缓冲区
                ByteBuffer buffer = ByteBuffer.allocate(1024);
    
                //将通道中的数据存入缓冲区中
                while(inChannel.read(buffer) != -1){
                    //切换到读取缓冲区数据的模式
                    buffer.flip();
                    //将缓冲区的数据写到通道中
                    outChannel.write(buffer);
                    //清空缓冲区
                    buffer.clear();
                }
            }catch (IOException e){
                e.printStackTrace();
            }finally {
                if (inChannel == null) {
                    try {
                        inChannel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (outChannel == null) {
                    try {
                        outChannel.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (fis == null) {
                    try {
                        fis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (fos == null) {
                    try {
                        fos.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
    

    使用直接缓冲区完成文件的复制(效率高)

        @org.junit.Test
        public void test4(){
            try {
                //StandardOpenOption.READ这里的读写要和下面的缓冲区的FileChannel.MapMode.READ_ONLY一致
                FileChannel inChannel = FileChannel.open(Paths.get("D:\\WorkSpace\\WorkSpace\\Java_idea\\NIO\\nio\\src\\main\\resources\\1.txt"), StandardOpenOption.READ);
                //StandardOpenOption.CREATE_NEW,创建新文件,文件已存在的话就报错,
                //StandardOpenOption.CREATE,创建新文件,覆盖老文件
                FileChannel outChannel = FileChannel.open(Paths.get("D:\\WorkSpace\\WorkSpace\\Java_idea\\NIO\\nio\\src\\main\\resources\\3.txt"), StandardOpenOption.READ,StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW);
    
                //这里的FileChannel.MapMode.READ_ONLY要和上面的StandardOpenOption.READ一致
                MappedByteBuffer inMappedBuf = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
                MappedByteBuffer outMappedBuf = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());
    
                byte[] dst = new byte[inMappedBuf.limit()];
                inMappedBuf.get(dst);
                outMappedBuf.put(dst);
    
                inChannel.close();
                outChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    

    复制文件(通道之间的数据传输(直接缓冲区))

        @org.junit.Test
        public void test5() throws IOException {
            //StandardOpenOption.READ这里的读写要和下面的缓冲区的FileChannel.MapMode.READ_ONLY一致
            FileChannel inChannel = FileChannel.open(Paths.get("D:\\WorkSpace\\WorkSpace\\Java_idea\\NIO\\nio\\src\\main\\resources\\1.txt"), StandardOpenOption.READ);
            //StandardOpenOption.CREATE_NEW,创建新文件,文件已存在的话就报错,
            //StandardOpenOption.CREATE,创建新文件,覆盖老文件
            FileChannel outChannel = FileChannel.open(Paths.get("D:\\WorkSpace\\WorkSpace\\Java_idea\\NIO\\nio\\src\\main\\resources\\5.txt"), StandardOpenOption.READ,StandardOpenOption.WRITE, StandardOpenOption.CREATE_NEW);
            
            inChannel.transferTo(0,inChannel.size(),outChannel);
            //或者,效果和上一行一样
            outChannel.transferFrom(inChannel,0,inChannel.size());
            
            inChannel.close();
            outChannel.close();
        }
    

    6. 分散与聚集

    分散读取:

    将通道数据分散到多个缓冲区中


    聚集写入:

    将多个缓冲区中的数据聚集到通道中


        @org.junit.Test
        public void test6() throws IOException {
            RandomAccessFile randomAccessFile = new RandomAccessFile("D:\\WorkSpace\\WorkSpace\\Java_idea\\NIO\\nio\\src\\main\\resources\\1.txt", "rw");
    
            FileChannel channel1 = randomAccessFile.getChannel();
    
            ByteBuffer buffer1 = ByteBuffer.allocate(100);
            ByteBuffer buffer2 = ByteBuffer.allocate(1024);
    
            ByteBuffer[] byteBuffers = {buffer1,buffer2};
            //分散读取,依次读取到buffer中
            channel1.read(byteBuffers);
    
            buffer1.flip();
            buffer2.flip();
    
            System.out.println(new String(buffer1.array(),0,buffer1.limit()));
            System.out.println("------------");
            System.out.println(new String(buffer2.array(),0,buffer2.limit()));
    
            RandomAccessFile randomAccessFile2 = new RandomAccessFile("D:\\WorkSpace\\WorkSpace\\Java_idea\\NIO\\nio\\src\\main\\resources\\6.txt", "rw");
            FileChannel channel2 = randomAccessFile2.getChannel();
            //聚集写入
            channel2.write(byteBuffers);
        }
    

    7. 阻塞式NIO(网络IO TCP)

        @org.junit.Test
        public void client() throws IOException {
            SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            FileChannel fileChannel = FileChannel.open(Paths.get("D:\\WorkSpace\\WorkSpace\\Java_idea\\NIO\\nio\\src\\main\\resources\\1.txt"), StandardOpenOption.READ);
            while (fileChannel.read(buffer) != -1){
                buffer.flip();
                socketChannel.write(buffer);
                buffer.clear();
            }
    
            socketChannel.close();
            fileChannel.close();
        }
    
        @org.junit.Test
        public void server() throws IOException {
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    
            serverSocketChannel.bind(new InetSocketAddress(9898));
    
            SocketChannel socketChannel = serverSocketChannel.accept();
    
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
    
            while (socketChannel.read(byteBuffer) != -1){
                System.out.println(new String(byteBuffer.array(),0,byteBuffer.limit()));
                System.out.println("----");
                byteBuffer.clear();
            }
    
            serverSocketChannel.close();
            socketChannel.close();
        }
    

    8. 非阻塞式NIO(网络IO TCP)

    image.png
        @org.junit.Test
        public void client() throws IOException {
            SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1", 9898));
    
            //切换成非阻塞模式
            socketChannel.configureBlocking(false);
    
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            FileChannel fileChannel = FileChannel.open(Paths.get("D:\\WorkSpace\\WorkSpace\\Java_idea\\NIO\\nio\\src\\main\\resources\\1.txt"), StandardOpenOption.READ);
            while (fileChannel.read(buffer) != -1){
                buffer.flip();
                socketChannel.write(buffer);
                buffer.clear();
            }
    
            socketChannel.close();
            fileChannel.close();
        }
    
        @org.junit.Test
        public void server() throws IOException {
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    
            //切换成非阻塞模式
            serverSocketChannel.configureBlocking(false);
    
            serverSocketChannel.bind(new InetSocketAddress(9898));
    
            //获取选择器
            Selector selector = Selector.open();
    
            //将通道注册到选择器上,并且指定监听事件,可选多个
            serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
    
            //轮询获取选择器上已经准备就绪的事件
            while (selector.select()>0){
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while (iterator.hasNext()) {
                    //获取准备就绪的事件
                    SelectionKey key = iterator.next();
                    System.out.println(key);
    
                    if (key.isAcceptable()){
                        SocketChannel socketChannel = serverSocketChannel.accept();
    
                        //切换非阻塞模式
                        socketChannel.configureBlocking(false);
                        //注册
                        socketChannel.register(selector,SelectionKey.OP_READ);
                    }else if (key.isReadable()){
                        SocketChannel channel = (SocketChannel) key.channel();
                        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
    
                        while (channel.read(byteBuffer) != -1){
                            System.out.println(new String(byteBuffer.array(),0,byteBuffer.limit()));
                            System.out.println("----");
                            byteBuffer.clear();
                        }
                        channel.close();
                    }
                    iterator.remove();
                }
            }
            serverSocketChannel.close();
        }
    

    9 非阻塞式NIO(网络IO UDP)

        @org.junit.jupiter.api.Test
        public void client2() throws IOException {
            DatagramChannel datagramChannel = DatagramChannel.open();
            datagramChannel.configureBlocking(false);
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            Scanner scanner = new Scanner(System.in);
            while (scanner.hasNext()){
                String str = scanner.next();
                buffer.put(str.getBytes());
                buffer.flip();
                datagramChannel.send(buffer,new InetSocketAddress("127.0.0.1",9898));
                buffer.clear();
            }
            datagramChannel.close();
        }
    
        @org.junit.Test
        public void server2() throws IOException {
            DatagramChannel datagramChannel = DatagramChannel.open();
            datagramChannel.configureBlocking(false);
    
            datagramChannel.bind(new InetSocketAddress(9898));
    
            Selector selector = Selector.open();
    
            datagramChannel.register(selector, SelectionKey.OP_READ);
    
            while (selector.select()>0){
                Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
                while (keys.hasNext()){
                    SelectionKey sk = keys.next();
                    if (sk.isReadable()){
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        datagramChannel.receive(buffer);
                        buffer.flip();
                        System.out.println(new String(buffer.array(),0,buffer.limit()));
                        buffer.clear();
                    }
                }
                keys.remove();
            }
        }
    

    相关文章

      网友评论

        本文标题:JAVA NIO

        本文链接:https://www.haomeiwen.com/subject/bnvkgqtx.html