三种IO的区别

BIO(Blocking I/O) :同步阻塞I/O模式 一个人只去厨房烧水且坐等烧水成功

NIO(New I/O):同步非阻塞模式 一个人只去厨房烧水且不定期来查看(轮询)

AIO (Asynchronous I/O):异步非阻塞 I/O 模型 一个人只去厨房烧水且去看电视且烧水成功之后有提醒

public class BioFileDemo {
    public static void main(String[] args) {
        BioFileDemo demo = new BioFileDemo();
        demo.writeFile();
        demo.readFile();
    }
// 使用 BIO 写入文件
public void writeFile() {
    String filename = "logs/itwanger/paicoding.txt";
    try {
        FileWriter fileWriter = new FileWriter(filename);
        BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
​
        bufferedWriter.write("学编程就上技术派");
        bufferedWriter.newLine();
​
        System.out.println("写入完成");
        bufferedWriter.close();
        fileWriter.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
​
// 使用 BIO 读取文件
public void readFile() {
    String filename = "logs/itwanger/paicoding.txt";
    try {
        FileReader fileReader = new FileReader(filename);
        BufferedReader bufferedReader = new BufferedReader(fileReader);
​
        String line;
        while ((line = bufferedReader.readLine()) != null) {
            System.out.println("读取的内容: " + line);
        }
​
        bufferedReader.close();
        fileReader.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
}

展示了如何使用 Java 中的传统阻塞 I/O(BIO)对文件进行读写操作。在 writeFile() 方法中,我们首先创建一个 FileWriter 对象,并使用 BufferedWriter 进行缓冲写入。接着,使用 bufferedWriter.write() 方法将字符串写入文件,然后调用 bufferedWriter.newLine() 方法添加换行符。最后,关闭 BufferedWriter 和 FileWriter。在 readFile() 方法中,我们创建一个 FileReader 对象,并使用 BufferedReader 进行缓冲读取。然后通过调用 bufferedReader.readLine() 方法循环读取文件内容,直到返回 null 表示读取完毕。最后,关闭 BufferedReader 和 FileReader

`public class NioFileDemo { public static void main(String[] args) { NioFileDemo demo = new NioFileDemo(); demo.writeFile(); demo.readFile(); }

// 使用 NIO 写入文件
public void writeFile() {
    Path path = Paths.get("logs/itwanger/paicoding.txt");
    try {
        FileChannel fileChannel = FileChannel.open(path, EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.WRITE));
​
        ByteBuffer buffer = StandardCharsets.UTF_8.encode("学编程就上技术派");
        fileChannel.write(buffer);
​
        System.out.println("写入完成");
        fileChannel.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
​
// 使用 NIO 读取文件
public void readFile() {
    Path path = Paths.get("logs/itwanger/paicoding.txt");
    try {
        FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ);
        ByteBuffer buffer = ByteBuffer.allocate(1024);
​
        int bytesRead = fileChannel.read(buffer);
        while (bytesRead != -1) {
            buffer.flip();
            System.out.println("读取的内容: " + StandardCharsets.UTF_8.decode(buffer));
            buffer.clear();
            bytesRead = fileChannel.read(buffer);
        }
​
        fileChannel.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

}

示例演示了如何使用 NIO 的 FileChannel 对文件进行读写操作。在 writeFile() 方法中,我们首先打开文件通道并指定创建和写入选项。接着,将要写入的字符串转换为 ByteBuffer,然后使用 fileChannel.write() 方法将其写入文件。在 readFile() 方法中,我们打开文件通道并指定读取选项,然后创建一个 ByteBuffer 用于存储读取到的数据。使用 fileChannel.read() 方法循环读取文件内容,直到返回 -1 表示读取完毕。在循环中,我们翻转缓冲区,将其解码为字符串并打印,然后清空缓冲区以进行下一次读取。最后,关闭文件通道

模型不同:流 vs. 缓冲区

1.这是两者最根本的区别。

  • BIO (面向流)BioFileDemo中,你操作的是ReaderWriter。你可以把流(Stream)*想象成一根*水管。数据就像水一样,从一端连续不断地流到另一端。你只能从水管的一头接水(读)或另一头灌水(写),而且是按顺序的,你无法跳到水管中间去操作。readLine()这个方法非常方便,它帮你处理了所有细节,你只需要拿一行处理好的字符串就行。

  • NIO (面向缓冲区)NioFileDemo中,你操作的是FileChannelByteBuffer

    • 通道 (Channel) 像一条铁路轨道,连接着你的程序和文件。

    • 缓冲区 (Buffer) 像一列火车

    你不能直接在轨道上装卸货物(数据)。所有的数据传输都必须通过火车(Buffer)来中转。

    • 读取时:你命令火车(Buffer)去文件(Channel)那里装货,fileChannel.read(buffer)。火车装满回来后,你再从火车上卸货。

    • 写入时:你先把货物装上火车,ByteBuffer.encode(...)。然后命令火车沿着轨道开到目的地去卸货,fileChannel.write(buffer)

2.NIO (复杂但控制力强)** 你需要手动处理更多细节,这增加了复杂性,但也给了你更大的控制权。在readFile()方法中体现得尤为明显:

  1. ByteBuffer.allocate(1024): 你需要手动创建并指定缓冲区的大小

  2. buffer.flip(): 这是NIO中的一个关键操作。当fileChannel.read(buffer)把数据读入缓冲区后,缓冲区处于“写模式”。你需要调用flip()将它“翻转”为“读模式”,这样你的代码才能从缓冲区里读取数据。

  3. StandardCharsets.UTF_8.decode(buffer): 因为缓冲区处理的是原始的字节(byte),你需要手动进行编码和解码

  4. buffer.clear(): 在循环的末尾,你需要调用clear()清空缓冲区,为下一次read操作做准备。

`public class AioDemo {

public static void main(String[] args) {
    AioDemo demo = new AioDemo();
    demo.writeFile();
    demo.readFile();
}
​
// 使用 AsynchronousFileChannel 写入文件
public void writeFile() {
    // 使用 Paths.get() 获取文件路径
    Path path = Paths.get("logs/itwanger/paicoding.txt");
    try {
        // 用 AsynchronousFileChannel.open() 打开文件通道,指定写入和创建文件的选项。
        AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
​
        // 将要写入的字符串("学编程就上技术派")转换为 ByteBuffer。
        ByteBuffer buffer = StandardCharsets.UTF_8.encode("学编程就上技术派");
        // 调用 fileChannel.write() 方法将 ByteBuffer 中的内容写入文件。这是一个异步操作,因此需要使用 Future 对象等待写入操作完成。
        Future<Integer> result = fileChannel.write(buffer, 0);
        // 等待写操作完成
        result.get();
​
        System.out.println("写入完成");
        fileChannel.close();
    } catch (IOException | InterruptedException | java.util.concurrent.ExecutionException e) {
        e.printStackTrace();
    }
}
​
// 使用 AsynchronousFileChannel 读取文件
public void readFile() {
    Path path = Paths.get("logs/itwanger/paicoding.txt");
    try {
        // 指定读取文件的选项。
        AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.READ);
        // 创建一个 ByteBuffer,用于存储从文件中读取的数据。
        ByteBuffer buffer = ByteBuffer.allocate(1024);
​
        // 调用 fileChannel.read() 方法从文件中异步读取数据。该方法接受一个 CompletionHandler 对象,用于处理异步操作完成后的回调。
        fileChannel.read(buffer, 0, buffer, new CompletionHandler<Integer, ByteBuffer>() {
            @Override
            public void completed(Integer result, ByteBuffer attachment) {
                // 在 CompletionHandler 的 completed() 方法中,翻转 ByteBuffer(attachment.flip()),然后使用 Charset.forName("UTF-8").decode() 将其解码为字符串并打印。最后,清空缓冲区并关闭文件通道。
                attachment.flip();
                System.out.println("读取的内容: " + StandardCharsets.UTF_8.decode(attachment));
                attachment.clear();
                try {
                    fileChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
​
            @Override
            public void failed(Throwable exc, ByteBuffer attachment) {
                // 如果异步读取操作失败,CompletionHandler 的 failed() 方法将被调用,打印错误信息。
                System.out.println("读取失败");
                exc.printStackTrace();
            }
        });
​
        // 等待异步操作完成
        Thread.sleep(1000);
​
    } catch (IOException | InterruptedException e) {
        e.printStackTrace();
    }
}

}`

BIO(Blocking I/O):采用阻塞式 I/O 模型,线程在执行 I/O 操作时被阻塞,无法处理其他任务,适用于连接数较少且稳定的场景。

NIO(New I/O 或 Non-blocking I/O):使用非阻塞 I/O 模型,线程在等待 I/O 时可执行其他任务,通过 Selector 监控多个 Channel 上的事件,提高性能和可伸缩性,适用于高并发场景。

AIO(Asynchronous I/O):采用异步 I/O 模型,线程发起 I/O 请求后立即返回,当 I/O 操作完成时通过回调函数通知线程,进一步提高了并发处理能力,适用于高吞吐量场景。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐