Java标准IO与Java NIO

新的输入/输出(NIO)文库与JDK 1.4引入。从原始I / O遗留的地方接起,NIO用标准Java代码提供了高速,面向块的I / O。通过定义用于保存数据的类,并通过按块处理该数据,NIO可以利用低级优化的优势,而无需使用本机代码,原始I / O包就无法实现。

在本教程中,我将着重于确定最明显的区别,在决定在下一个项目中使用哪个区别之前,您必须知道这些区别。

回收旧的IO设备

I / O(输入/输出)是指计算机与世界其他地方之间或单个程序与计算机其余部分之间的接口。单个程序通常会为他们完成大部分工作。在Java编程中,直到最近才使用 Stream 隐喻来执行I / O。所有I / O都被视为单个字节通过一个称为Stream的对象一次移动。 Stream I / O用于联系外界。它还在内部使用,用于将对象转换为字节,然后再转换为对象。

介绍NIO

创建 NIO的目的是允许Java程序员无需编写自定义本机代码即可实现高速I / O。NIO将最耗时的I / O活动(即填充和清空缓冲区)移回操作系统,从而极大地提高了速度。

如果以上介绍让您感到口渴,请不要担心,在我们前进的过程中您是否会感到更好。让我们从发现差异开始。

识别IO和NIO之间的差异

1)IO Stream 与NIO块

原始I / O库(可在java.io. *中找到)和NIO之间最重要的区别与数据的打包和传输方式有关。如前所述,原始I / O处理 Stream 中的数据,而NIO处理块中的数据。

面向 Stream 的I / O系统一次处理一个或多个字节的数据。 InputStream 产生一个字节的数据,而 OutputStream 消耗一个字节的数据。为 Stream 数据创建过滤器非常容易。将几个过滤器链接在一起也是相对简单的,这样每个过滤器都能发挥自己的作用,相当于一个单一的复杂处理机制。重要的是字节不会在任何地方缓存。此外,您不能在 Stream 中的数据中来回移动。如果需要来回移动从 Stream 中读取的数据,则必须先将其缓存在缓冲区中。

面向块的I / O系统按块处理数据。每个操作一步就产生或消耗一个数据块。通过块处理数据可能
比通过( Stream 式传输)字节处理数据快得多。您可以根据需要在缓冲区中来回移动。这使您在处理过程中更具灵活性。但是,您还需要检查缓冲区是否包含您需要的所有数据,以便对其进行完全处理。并且,您需要确保在将更多数据读入缓冲区时,不要覆盖尚未处理的缓冲区中的数据。但是面向块的I / O缺少面向 Stream 的I / O的一些优雅和简单性。

阅读更多: 使用Java NIO读取文件的3种方法

2)同步与异步IO

Java IO的各种 Stream 正在阻塞或同步。这意味着,当线程调用read()或write()时,该线程将被阻塞,直到有一些数据要读取或数据被完全写入为止。在此期间,线程将处于阻塞状态。这被认为是在现代语言中引入多线程的一个很好的坚实理由。

在异步IO中,线程可以请求将某些数据写入通道,但不等待将其完全写入。然后线程可以继续运行,同时执行其他操作。通常,这些线程将空闲时间花费在未阻塞IO调用上的时间,通常同时在其他通道上执行IO。也就是说,单个线程现在可以管理输入和输出的多个通道。

同步程序通常不得不诉诸于轮询或创建许多线程来处理大量连接。使用异步I / O,您可以在任意数量的通道上侦听I / O事件,而无需轮询且无需额外的线程。

异步I / O中的中心对象称为选择器。选择器是您对各种I / O事件感兴趣的地方,它是告诉您何时发生这些事件的对象。因此,我们需要做的第一件事是创建一个选择器:

Selector selector = Selector.open();

稍后,我们将在各种通道对象上调用register()方法,以使我们对那些对象内发生的I / O事件感兴趣。register()的第一个参数始终是选择器。

阅读更多:如何在java NIO中定义路径

3)IO与NIO API

猜测使用NIO时API调用与使用IO时看起来不同的猜测是没有根据的。在NIO中,必须先从一个InputStream中读取数据,然后再对其进行处理,而不仅仅是从InputStream中逐字节读取数据。

使用标准IO的示例代码

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
 
public class WithoutNIOExample
{
    public static void main(String[] args)
    {
        BufferedReader br = null;
        String sCurrentLine = null;
        try
        {
            br = new BufferedReader(
            new FileReader("test.txt"));
            while ((sCurrentLine = br.readLine()) != null)
            {
                System.out.println(sCurrentLine);
            }
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        finally
        {
            try
            {
                if (br != null)
                br.close();
            } catch (IOException ex)
            {
                ex.printStackTrace();
            }
        }
    }
}

使用NIO的示例代码

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
 
public class ReadFileWithFixedSizeBuffer
{
    public static void main(String[] args) throws IOException
    {
        RandomAccessFile aFile = new RandomAccessFile
                ("test.txt", "r");
        FileChannel inChannel = aFile.getChannel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        while(inChannel.read(buffer) > 0)
        {
            buffer.flip();
            for (int i = 0; i < buffer.limit(); i++)
            {
                System.out.print((char) buffer.get());
            }
            buffer.clear(); // do something with the data and clear/compact it.
        }
        inChannel.close();
        aFile.close();
    }
}

摘要

NIO允许您仅使用一个(或更少)线程来管理多个通道,但是代价是解析数据可能比使用标准IO从阻塞 Stream 中读取数据时要复杂得多。

如果您需要同时管理数千个打开的连接(每个连接仅发送少量数据),例如聊天服务器,则在NIO中实现该服务器可能是一个优势。同样,如果您需要保持与其他计算机的大量开放连接,例如在P2P网络中,则使用单个线程来管理所有出站连接可能是一个优势。

如果您只有很少的连接且带宽很高,那么一次发送大量数据,则应该选择标准IO服务器实现。

学习愉快!

saigon has written 1440 articles

Leave a Reply