Interview - Java Input and Output Three Brothers Competition: Comparative Analysis of IO, NIO, and AIO

1. History of Java I/O Development                

Java IO (Input/Output) is an API for reading and writing data in the Java language. It provides a series of classes and interfaces for reading and writing various types of data. The following is a brief introduction to the history of Java IO development:

  1. JDK 1.0 (1996) The original Java IO only supported byte streams (InputStream, OutputStream) and character streams (Reader, Writer), based on the blocking IO (BIO) model.
  2. JDK 1.1 (1997) JDK 1.1 introduced the NIO (New IO) package, which supports concepts such as buffer (Buffer) and channel (Channel), provides a more efficient IO operation mode, and can realize non-blocking IO (NIO) model.
  3. JDK 1.4 (2002) JDK 1.4 added NIO.2 API, also known as Java NIO with buffers, which provides more powerful file processing functions and more efficient IO operations.
  4. JDK 7 (2011) JDK 7 introduced an improved version of NIO.2 - NIO.2 with Completion Ports, also known as AIO (Asynchronous IO), which supports asynchronous IO and has advantages in processing a large number of concurrent requests.

Here are the differences between the three:

  1. Blocking IO (BIO) BIO is the original IO model of Java, which uses blocking to read and write data. That is, when a thread is performing an IO operation, if there is no data to read, the thread will block and wait until there is data to read or it times out. BIO is suitable for scenarios where the number of connections is relatively small and fixed, but the concurrency capability is insufficient.
  2. Non-blocking IO (NIO) NIO is a new IO model introduced by Java 1.4. It uses a multiplexer (Selector) mechanism to manage multiple channels at the same time through a small number of threads, achieving the effect of a single thread processing multiple requests at the same time. . NIO has high concurrency, high throughput, and higher reliability, and is suitable for scenarios with a large number of connections and a short connection time.
  3. Asynchronous IO (AIO) AIO is an IO model supported by Java 1.7. It uses an event-driven approach to read and write data. When the data is ready, it is processed in the callback function. Unlike NIO, AIO's read and write operations are asynchronous, and there is no need to check whether the data is ready by polling. AIO is suitable for scenarios with a large number of connections, long connection times, and many read and write operations.

2、Java IO

2.1 Introduction

In Java programming, IO (Input/Output) operations are very common operations, which involve file reading and writing, network communication, etc. Java provides various classes to support these operations. This article will start with the basics of IO, gradually deepen, and introduce all aspects of Java IO.

2.2 Basic concepts

2.2.1 Input stream and output stream

In Java, input stream (InputStream) and output stream (OutputStream) are two important abstract classes. The input stream indicates the source of input data, which can be a file, network connection, pipeline, etc.; the output stream indicates the destination of the output data, which can be a file, network connection, pipeline, etc. The input stream and output stream are used in a similar way, both by creating a stream object and then using the corresponding method interface for read and write operations.

2.2.2 Byte stream and character stream

IO operations in Java can also be divided into two types: byte stream and character stream. The byte stream operates in units of bytes and is suitable for processing binary data, such as images, audio, etc.; while the character stream operates in units of characters (char), and is suitable for processing text data, such as text files. In Java, the byte stream is mainly realized by the InputStream and OutputStream classes and their subclasses, while the character stream is mainly realized by the Reader and Writer classes and their subclasses.

2.2.3 Buffered Streams

When performing IO operations, we may need to perform frequent read and write operations, and frequent reads and writes may cause performance problems. In order to solve this problem, Java provides a buffered stream (Buffered Stream) to improve the efficiency of IO operations. Buffered streams can reduce the number of accesses to underlying resources through the internal cache area, thereby improving the efficiency of data reading and writing.

2.3 Use of Java IO

2.3.1 File reading and writing

Reading and writing files in Java is one of the most common operations in development. Here is an example of reading the contents of a file and outputting it:

    try (FileInputStream fis = new FileInputStream("test.txt");
         InputStreamReader isr = new InputStreamReader(fis);
         BufferedReader br = new BufferedReader(isr)) {
        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

In this example, we use classes such as FileInputStream, InputStreamReader and BufferedReader to complete the file reading. First, we create an input stream object through the FileInputStream class, and specify the name of the file to be read; then convert the byte stream into a character stream through InputStreamReader, and then read the text content line by line through BufferedReader.

Similarly, writing files in Java is also very simple. Here is an example of writing to a file:

try (FileOutputStream fos = new FileOutputStream("test.txt");
     OutputStreamWriter osw = new OutputStreamWriter(fos);
     BufferedWriter bw = new BufferedWriter(osw)) {
    bw.write("Hello, world!");
} catch (IOException e) {
    e.printStackTrace();
}

In this example, we use classes such as FileOutputStream, OutputStreamWriter and BufferedWriter to complete the writing of files. First, we create an output stream object through the FileOutputStream class, and specify the name of the file to be written; then convert the byte stream into a character stream through the OutputStreamWriter, and then write the text content line by line through the BufferedWriter.

3、Java NIO

3.1 Introduction

Java NIO (New IO) is a new IO API introduced by Java SE 1.4, which provides more efficient and flexible IO operations than traditional IO. Compared with traditional IO, the advantage of Java NIO is that it supports features such as non-blocking IO and selectors (Selector), which can better support high-concurrency and high-throughput application scenarios. This article will start with the basics of NIO, and gradually deepen, introducing all aspects of Java NIO.

3.2 Core Concepts

3.2.1 Selector

The selector is an important component in Java NIO. It can be used to monitor the read and write events of multiple channels at the same time, and respond immediately when an event occurs. The selector can realize the effect of single-thread monitoring multiple channels, thereby improving system throughput and operating efficiency.

3.2.2 Channel (Channel)

A channel is an object for reading and writing data, similar to a stream in Java IO. Unlike streams, channels can be read and written non-blockingly, and read and write operations can be performed concurrently. Channels are divided into two types: FileChannel and SocketChannel, which are used for files and networks respectively

communication.

3.2.3 Buffer (Buffer)

In Java NIO, all data is transferred through buffer objects. A buffer is a contiguous block of memory that holds data that needs to be read or written. The buffer object contains some state variables, such as capacity, limit, position, etc., which are used to control the reading and writing of data.

3.3 Use of Java NIO

3.3.1 Buffer operations

Buffer operations in Java NIO mainly include two operations: data reading and data writing. Here is a simple buffer read example:

ByteBuffer buffer = ByteBuffer.allocate(1024);
try (FileChannel channel = new FileInputStream("test.txt").getChannel()) {
    int bytesRead = channel.read(buffer);
    while (bytesRead != -1) {
        buffer.flip();
        while (buffer.hasRemaining()) {
            System.out.print((char) buffer.get());
        }
        buffer.clear();
        bytesRead = channel.read(buffer);
    }
} catch (IOException e) {
    e.printStackTrace();
}

In this example, we use the FileChannel class and the ByteBuffer class to read the file. First, we created an input stream object through the FileInputStream class, and then obtained the corresponding channel object through the getChannel() method; then, we created a ByteBuffer object with a capacity of 1024 bytes, and called the read() method from the channel Read data and save the read data in the buffer. After the reading is completed, we switch the buffer to read mode through the flip() method, and use the hasRemaining() and get() methods to read the data one by one; finally clear the buffer through the clear() method to prepare for the next round of reading Pick.

The buffer write operation in Java NIO is also very similar, the following is a simple buffer write example:

ByteBuffer buffer = ByteBuffer.wrap("Hello, world!".getBytes());
try (FileChannel channel = new FileOutputStream("test.txt").getChannel()) {
    channel.write(buffer);
} catch (IOException e) {
    e.printStackTrace();
}

In this example, we use the FileChannel class and ByteBuffer class to complete the file writing. First, we convert the string to a ByteBuffer object through the ByteBuffer.wrap() method; then, we create an output stream object through the FileOutputStream class, and then obtain the corresponding channel object through the getChannel() method; then, we call write( ) method writes the data in the buffer into the channel to complete the file writing operation.

3.3.2 Channel Operation

The channel (Channel) in Java NIO is an object used for data transmission. Channels can be connected to source or destination nodes for reading and writing data. Unlike traditional Java IO, channels in NIO can read and write data in a non-blocking manner, thereby improving the performance and concurrent processing capabilities of applications.

To use a channel for read and write operations, you first need to open the channel. There are many types of channels in Java NIO, and each channel provides its own opening method. For example, to open a file channel, you can obtain the corresponding FileChannel object through the FileInputStream or FileOutputStream object, as follows:

    FileChannel channel = new FileInputStream("file.txt").getChannel();

Reading data Once the channel is opened, you can start reading data. In NIO, data is transferred through buffers. To read data, you need to store the data in a buffer and then read the data from the buffer. Here is an example of a simple read operation:

ByteBuffer buffer = ByteBuffer.allocate(1024);
// 从通道中读取数据
int bytesRead = channel.read(buffer);
while (bytesRead != -1) {
    // 切换为读模式
    buffer.flip();
    // 读取缓冲区中的数据
    while (buffer.hasRemaining()) {
        System.out.print((char) buffer.get());
    }
    // 清空缓冲区
    buffer.clear();
    bytesRead = channel.read(buffer);
}

In this example, we first create a ByteBuffer object with a capacity of 1024 bytes, and use the channel.read() method to read data from the file channel. After reading the data, we switch the ByteBuffer to read mode, and then use the hasRemaining() and get() methods to read the data in the buffer one by one.

Writing data To write data, it is also necessary to store the data in the buffer, and then write the data in the buffer to the channel. Here is an example of a simple write operation:

ByteBuffer buffer = ByteBuffer.wrap("Hello, world!".getBytes());
// 将数据写入通道
channel.write(buffer);

In this example, we first use the ByteBuffer.wrap() method to convert the string to a ByteBuffer object, and then write the data in the ByteBuffer to the channel through the channel.write() method.

4、Java AIO

Java AIO (Asynchronous IO) is an IO model based on events and callbacks. Compared with Java BIO (Blocking IO) and Java NIO (Non-blocking IO), it has higher concurrency, higher throughput and more high reliability. This article will introduce the principles, characteristics and applications of Java AIO.

4.1 Principle of Java AIO

Java AIO uses asynchronous IO to read and write data. Unlike Java NIO, it does not need to check whether the data is ready through polling, but is completed by the operating system. When the data is ready, the operating system notifies the application and handles it in the callback function.

AIO uses three core components: AsynchronousChannel, CompletionHandler and
AsynchronousServerSocketChannel. Among them, AsynchronousChannel is a channel for reading/writing data, CompletionHandler is a callback method when the I/O operation is completed, and AsynchronousServerSocketChannel is an asynchronous server-side socket channel for listening to client connection requests.

When the data is ready, the operating system will notify the application and execute the callback method when the I/O operation is completed in the callback function. In this way, the situation of thread blocking waiting for the completion of I/O operation is avoided, thereby improving the efficiency and concurrent processing capability of the program.

4.2 Features of Java AIO

  1. High concurrency: Java AIO adopts asynchronous IO mode for data read and write operations, which can achieve high concurrent processing capabilities.
  2. High throughput: Java AIO supports asynchronous read and write operations and can process multiple requests at the same time, thereby improving the efficiency and throughput of data read and write.
  3. High reliability: Since Java AIO uses asynchronous IO to read and write data, it can avoid thread blocking waiting for I/O operations to complete, thereby improving the reliability of the program.
  4. Simple and easy to use: Java AIO provides a simple and easy-to-use API, which can realize asynchronous IO operations without writing complex codes.

4.3 Application of Java AIO

Java AIO is suitable for scenarios that require a large number of concurrent connections, but each connection has little data interaction, such as message-based applications, remote procedure calls (RPC), etc. In these application scenarios, AIO can greatly improve the performance and concurrent processing capabilities of the program, thereby meeting the user's requirements for high throughput and low latency.

In addition, Java AIO can also be used to develop high-performance network servers, such as chat room servers, online game servers, etc. Because AIO supports asynchronous reading of input and output streams, it can process multiple client requests at the same time, effectively improving the server's concurrent processing capabilities.

4.4 Summary

As a high-performance, high-concurrency IO model, Java AIO has many advantages, but it also has some disadvantages. For example, for small-load connections, the overhead of AIO may lead to performance degradation. Therefore, in practical applications, various factors need to be weighed and evaluated and selected according to actual needs.

5. Related interview questions

1. What are Java IO and NIO?
Java IO (Input/Output) is a traditional input and output operation in Java, using byte stream and character stream for data transmission.
Java NIO (New Input/Output) is a new input and output API introduced by Java 1.4, which processes data more efficiently.
2. What is blocking and non-blocking IO?
Blocking IO (Blocking IO) will wait until the IO is completed during the IO operation, during which no other operations can be performed.
Non-blocking IO (Non-blocking IO) does not wait for the IO operation, but returns the result immediately. If the IO is not completely completed, you can continue to do other things.
3. What is a buffer zone? What types of buffers are there?
The buffer is an array used to store data, which needs to be read and written through the buffer when performing IO operations.
Java buffers are divided into byte buffers (ByteBuffer, CharBuffer, ShortBuffer, etc.) and direct buffers (DirectByteBuffer, DirectCharBuffer, DirectShortBuffer, etc.).
4. What is a channel?
A channel is an object used for data transfer in NIO, which can be connected to a source or target node for reading and writing data.
5. What is a Selector?
A selector is an object in NIO, which can check whether they have events (such as readable, writable, etc.) by polling the channels registered on it, thus avoiding the problem of waiting for IO to complete in blocking IO .
6. What is the difference between Java IO and NIO?
Java IO performs data transmission based on streams, while NIO performs data transmission based on buffers and channels.
Java IO is blocking, while NIO can use blocking or non-blocking mode.
Java IO uses more threads, and each IO operation needs to create a thread, while NIO can use a single thread to handle multiple IO operations.
7. What is a file channel (FileChannel)?
A file channel is a channel in NIO for reading and writing files, which supports advanced functions such as random access and memory-mapped files.
8. Which is faster, Java IO or NIO?
In the case of large amounts of small data, Java IO may be faster because it can read all the data at once through the buffer.
In the case of a large number of large blocks of data, NIO may be faster because it can directly read data from disk into memory using zero-copy technology, reducing the overhead of data copying.



Finally, I recommend the drawing tools in the article:

​​​​​​​

​​​​​​​

ProcessOn​​​​​​​

Guess you like

Origin blog.csdn.net/citywu123/article/details/130075279