3、Java NIO

1、Java NIO介绍

  • non-blocking IO,从JDK1.4开始Java提供了一系列改进的IO新特性,统称为NIO(New IO),同步非阻塞;
  • 三大核心部分:Channel、Buffer、Selector
  • NIO是面向缓冲区(或是面向块编程的)。将数据读取到一个稍后处理的缓冲区,需要时可在缓冲区前后移动,增加了处理过程的灵活性;
  • NIO的非阻塞模式:使一个线程从某通道发送请求或者读取数据,但是仅能得到目前可用的数据,如果目前没有数据可用时,什么也不会获取,而不是保持线程阻塞,直至数据变得可以读取之前,线程可以做其他事情。非阻塞写也是如此,一个线程请求写入一些数据到某通道,不需要等待完全写入,该线程可以去做别的事情;
  • NIO可以做到使用一个线程处理多个操作。
  • HTTP2.0使用了多路复用技术,做到使用一个连接并发处理多个请求,而且并发请求的数量比HTTP1.1大了好几个数量级;

2、NIO与BIO比较

  • BIO以流的方式处理数据,NIO以块的方式处理数据,块IO的效率比流IO高很多;
  • BIO是阻塞的,NIO是非阻塞的;
  • BIO基于字节流和字符流进行操作,NIO基于channel和buffer进行操作,数据总是从通道读取到缓冲区,或者从缓冲区写道通道。selector用于监听多个通道的事件,因此使用单个线程就可以监听多个客户端通道;

3、NIO三大核心原理图

在这里插入图片描述

  • 每个channel对应一个buffer
  • selector对应一个线程,一个线程对应多个channel
  • 程序切换到哪个channel是由事件决定的;
  • selector会根据不同的事件在各个通道上切换;
  • buffer就是一个内存块,底层是一个数组;
  • 数据的读取写入是通过buffer,BIO中要么是输入流要么是输出流不能双向,但是NIO的buffer可以读可以写,需要flip方法切换;
  • channel是双向的;

4、Buffer

在这里插入图片描述
在这里插入图片描述

private int mark = -1;
private int position = 0;
private int limit;
private int capacity;

ByteBuffer

Java的基本数据类型都有一个Buffer类型与之对应,最常用的就是ByteBuffer类;

5、通道

  1. NIO的通道类似于流,但还有区别
  • 通道可以同时进行读写,而流只能读或者只能写
  • 通道可以实现异步读写数据
  • 通道可以从缓冲读数据,也可以写数据到缓冲;
  1. BIO的stream是单向的,FileInputStream对象只能读取数据,NIO的channel是双向的;
  2. channel在NIO中是一个接口;
  3. 常见的channel:FileChannel、DatagramChannel、ServerSocketChannel、SocketChannel(ServerSocketChannel类似于ServerSocket,SocketChannel类似于Socket);
  4. FileChannel用于文件的数据读写,DatagramChannel用于UDP的数据读写,ServerSocketChannel和SocketChannel用于TCP的数据读写;

5.1 FileChannel类

//从通道读取数据并放到缓冲区中;
public int read(ByteBuffer dst);
//把缓冲区的数据写道通道中
public long read(ByteBuffer[] dsts);
public long transferFrom(ReadableByteChannel src,
                  long position, long count);
public long transferTo(long position, long count,                                     WritableByteChannel target);

实例1:本地文件写数据
将字符串“hello,world”写入到file1.txt

import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class NIOFileChannel01 {
    
    
    public static void main(String[] args) throws Exception {
    
    
        String str="hello,world";
        FileOutputStream fileOutputStream=new FileOutputStream("file1.txt");
        //通过fileOutputStream获取对应的FileChannel
        FileChannel channel = fileOutputStream.getChannel();
        //创建一个缓冲区ByteBuffer
        ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
        byteBuffer.put(str.getBytes());
        //对byteBuffer进行flip
        byteBuffer.flip();
        //byteBuffer数据写入到channel
        channel.write(byteBuffer);
        fileOutputStream.close();
    }
}

实例2:本地文件读数据
将file1.txt的数据显示在屏幕上;

import java.io.File;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class NIOFileChannel02 {
    
    
    public static void main(String[] args) throws Exception {
    
    
        File file=new File("file1.txt");
        FileInputStream fileInputStream=new FileInputStream(file);
        
        //获取到fileInputStream对应的channel
        FileChannel channel = fileInputStream.getChannel();
        
        //创建缓冲区
        ByteBuffer byteBuffer=ByteBuffer.allocate((int)file.length());
        
        //将channel的数据读入到缓冲区
        channel.read(byteBuffer);
        
        System.out.println(new String(byteBuffer.array()));
        fileInputStream.close();
    }
}

使用一个buffer完成文件读取、写入

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class NIOFileChannel03 {
    
    
    //使用一个buffer完成文件读取写入
    public static void main(String[] args) throws Exception {
    
    
        FileInputStream fileInputStream=new FileInputStream("file1.txt");
        FileOutputStream fileOutputStream=new FileOutputStream("file2.txt");
        FileChannel channel1 = fileInputStream.getChannel();
        FileChannel channel2 = fileOutputStream.getChannel();

        ByteBuffer byteBuffer=ByteBuffer.allocate(10);
        //循环读取
        while(true){
    
    
            byteBuffer.clear();
            int read = channel1.read(byteBuffer);
            System.out.println("read="+read);
            if(read==-1)break;
            byteBuffer.flip();
            channel2.write(byteBuffer);
        }
        fileInputStream.close();
        fileOutputStream.close();
    }
}

使用transferFrom完成拷贝

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.channels.FileChannel;

public class NIOFileChannel04 {
    
    
    public static void main(String[] args) throws Exception{
    
    
        FileInputStream fileInputStream=new FileInputStream("me.jpg");
        FileOutputStream fileOutputStream=new FileOutputStream("me2.jpg");

        FileChannel channel1 = fileInputStream.getChannel();
        FileChannel channel2 = fileOutputStream.getChannel();

        //使用transferFrom完成拷贝
        channel2.transferFrom(channel1,0,channel1.size());
        channel1.close();
        channel2.close();
        fileInputStream.close();
        fileOutputStream.close();
    }
}

put和get应该是同一个类型

import java.nio.ByteBuffer;

public class NIOByteBufferPutGet {
    
    
    public static void main(String[] args) {
    
    
        ByteBuffer buffer=ByteBuffer.allocate(64);
        buffer.putInt(100);
        buffer.putLong(9);
        buffer.putChar('a');
        buffer.putShort((short)4);
        //取出
        buffer.flip();
        System.out.println(buffer.getInt());
        System.out.println(buffer.getLong());
        System.out.println(buffer.getChar());
        System.out.println(buffer.getShort());
    }
}

普通buffer转成只读buffer

import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class NIOFileChannel01 {
    
    
    public static void main(String[] args) throws Exception {
    
    
        String str="hello,world";
        FileOutputStream fileOutputStream=new FileOutputStream("file1.txt");
        //通过fileOutputStream获取对应的FileChannel
        FileChannel channel = fileOutputStream.getChannel();
        //创建一个缓冲区ByteBuffer
        ByteBuffer byteBuffer=ByteBuffer.allocate(1024);
        byteBuffer.put(str.getBytes());
        //对byteBuffer进行flip
        byteBuffer.flip();
        //byteBuffer数据写入到channel
        channel.write(byteBuffer);
        fileOutputStream.close();
    }
}

直接在内存中修改文件

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

public class MappedByteBufferTest {
    
    
    public static void main(String[] args) throws IOException {
    
    
        RandomAccessFile randomAccessFile = new RandomAccessFile("file1.txt", "rw");
        FileChannel channel = randomAccessFile.getChannel();
        MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_WRITE, 0, 5);
        map.put(0,(byte)'H');
        map.put(3,(byte)'9');
        map.put(5,(byte)'Y');
        randomAccessFile.close();
    }
}

猜你喜欢

转载自blog.csdn.net/qq_37935909/article/details/109027553