In network applications, it is generally possible to use synchronous I/O (blocking I/O) and non-blocking I/O for data communication in two different ways. The two approaches are not mutually exclusive and mutually replaceable. We can use one of the methods of communication separately in our usual applications, or we can mix the two modes of communication. In this article on what is non-blocking I/O and why this communication is used, a simple example is presented in the next article to demonstrate how to use non-blocking I/O for communication in a network application.
First, what is non-blocking I/O
We can call synchronous I/o a blocking I/O, and non-blocking I/O is called asynchronous I/O. A more commonly used term is used in this book: Synchronous I/O and non-blocking I/O. Although they are called different, the meaning is the same. Readers should pay attention to this when reading other books.
Before you explain what non-blocking I/O is, you should first understand what sync I/O is, and what this synchronization refers to. The concept of synchronization is primarily the process by which code is executed sequentially. As in the main method in Java programs, if you do not use multiple threads, the code in this method must be executed sequentially from the go. This is called synchronization. If you use multiple threads, there are different pieces of code that can be executed at the same time from a macro point of view, which is called Asynchrony. Synchronization concepts are similar in synchronous I/O, that is, in the I/O communication process, no other communication can be performed as long as the communication in one step is not completed. So what does this sync mean? To answer this question, let's first recall where there might be a blockage in the network traffic. If the reader looks at the previous chapter, he will know the answer. For clients, there are two places that can be blocked: connecting to the server (when the Connect method is invoked) and reading and writing data. There are also two places that can be blocked on the service side: Waits for client requests (when the Accept method is invoked) and reads and writes data (in general, write data is not blocked, but if the network environment is poor, the client and the service side of the write data operation can also occur blocking phenomenon). That is, the place where the timeout can be set may be blocked. Synchronization in synchronous I/O means that the program is waiting until the following two situations are in progress:
1. Connect to the server, read or write data, or wait for client requests to perform normally.
2. A timeout exception was thrown after waiting for the timeout period.
In the above we know what is synchronous I/O. The most obvious difference between non-blocking I/O and sync I/OS is that all the potentially blocked addresses of sync I/O are not blocked in non-blocking I/O. If the data cannot be read for the time being read. It will be returned immediately in non-blocking I/O so that the program can execute other code, and the system will continue to detect the unfinished read until it is ready to read.
Java provides a set of APIs in JDK1.4 and later versions to specialize in non-blocking I/O, and we can find related classes and interfaces in the Java.nio package and its child packages. Since this API is the new I/O API provided by JDK, it is also called New I/O, which is the origin of the package name NiO. This set of APIs consists of three main components: buffer (buffers), channel (channels), and core classes of non-blocking I/O. The details of these three sections will be covered later in this chapter.
Second, why use non-blocking I/O
In a network application that uses synchronous I/O, you must use multithreading if you want to handle multiple client requests at the same time, or if the client wants to communicate with multiple servers at the same time. In other words, each client request is assigned to a thread to handle separately. While this can meet our requirements, it also poses another problem. Because each thread is created, it allocates a certain amount of memory (also known as working memory) to the thread, and the operating system itself limits the total number of threads. If there are too many requests from the client, the server-side program may be overwhelmed to deny the client's request, and even the servers may become paralyzed.
Of course, you can use the thread pool (which will be explained in the third part) to ease the pressure on the server, but this does not solve the problem that the client refused to respond because of the intensive access. Although there is a request buffer on the server as a guarantee, the size of the buffer is limited (typically 50) and the client receives a DENIAL-OF-SERVICE message if the number of requests from the client is much higher.
In this case, using non-blocking I/O can solve the problem. Because programs that use non-blocking I/O are typically single-threaded (sometimes with a single thread for a program segment that uses non-blocking I/O, while the main thread is responsible for processing user input), the number of client requests received by the server does not increase as the number of worker threads increases. Therefore, the use of non-blocking I/O mode is not limited by the operating system's total number of threads, nor does it consume a large amount of server resources.
non-blocking I/O can reach the goal of processing a large number of client requests without consuming a large amount of server resources. However, this mode of communication does not completely replace synchronous I/O. Non-blocking I/O is not suitable for applications that need to remain connected as an FTP server (the reason will be explained in later chapters). Non-blocking I/O is more commonly used in the service side, because the client generally does not need to handle a large number of connections (except for some applications, elephant Baidu, Google's web Spider, need to download multiple pages at the same time, then need to establish a large number of connections in the client to meet demand) Server-side programs generally need to receive and process a large number of client requests, so it is necessary to use multithreading (using synchronous I/O) or non-blocking I/O to achieve this goal. If a server-side application handles client requests less, it might be better to use multithreading and sync I/O, as this approach is more flexible than non-blocking I/O.
Always put non-blocking I/O and network applications together in the front. In fact, non-blocking I/O is not equal to the network. We can also apply non-blocking I/O to non-network applications, such as File replication. Because synchronous I/O is based on byte throttling, non-blocking I/O is based on buffers and channels. Therefore, theoretically, the larger the file operation, the more the advantage of non-blocking I/O can be reflected. For smaller file operations, these two approaches are more efficient. According to the experiment, copy a 4G or so file, in general, non-blocking I/O way faster than synchronous I/O mode about 15%.