Nonblocking I/O

Nonblocking I O

Nonblocking I/O is one of the most important features of the new I/O API. Traditional stream-based I/O is limited by the speed of whatever it is you're reading or writing: the disk, the network, and so on. Very often code has to sit and wait for the disk or network to respond. This is especially problematic if the program has many different things to do. For instance, a network server may need to service hundreds or thousands of simultaneous clients. It doesn't want one slow connection to hold back all the others.

The traditional approach to this problem is to place each connection in a separate thread. One hundred simultaneous connections require one hundred threads. However, although threads are lighter-weight than processes, they still have nontrivial overhead. It takes time to set up and tear down each thread, and each one uses a finite quantity of system resources. For instance, in some versions of Windows, each thread has a megabyte of stack space. Thus, if you try to spawn 2,000 threads on a system with only a gigabyte of memory, you're going to start swapping pretty badly (and that's not even accounting for all the memory that's needed for anything other than thread stacks). Thread pools improve the situation, perhaps allowing you to handle twice as many simultaneous connections as you could otherwise, but not to the point where you can simply ignore thread overhead.

Fortunately, the new buffer- and channel-based I/O comes to the rescue. In addition to the synchronous blocking I/O explored in the last two chapters, NIO also supports nonblocking I/O. In nonblocking I/O, one thread can manage many different connections. Rather than fully processing each one in turn, the thread asks the channels which one is ready to be read or written without blocking. It then reads or writes a channel that it knows in advance won't block. Then it repeats the process. On high-volume servers, this can easily more than triple the number of clients one system can handle.

Nonblocking I/O is primarily relevant to network connections. Pipe channels that move data between two threads also support nonblocking I/O. File channels don't support it at all because file access doesn't block nearly as often as network channels do, and most modern disk controllers can fill a CPU with data fast enough to keep it satisfied. Furthermore, it's uncommon for one program to read or write hundreds of files simultaneously. However, on network servers, this usage pattern is the rule, not the exception.

Категории