Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Use select for high-speed networking

New Input/Output libraries speed up your server

  • Print
  • Feedback

Java uses an extremely elegant input/output (I/O) model, based on the idea of a stream. A stream is an object that produces or consumes a string of bytes. Streams can be chained together in conjunction with filtering routines and extended to handle other kinds of data. The stream model is very flexible, but not too fast. It's fine for many applications, but some systems require just about as much speed as the hardware can handle. Sometimes the stream model won't cut it.

The New Input/Output (NIO) libraries introduced in Java 2 Platform, Standard Edition (J2SE) 1.4 address this problem. NIO uses a buffer-oriented model. That is, NIO deals with data primarily in large blocks. This eliminates the overhead caused by the stream model and even makes use of OS-level facilities, where possible, to maximize throughput.

First I look at how NIO works, and then I put it to use in a high-speed server application.

Note: You can download this article's source code from Resources.

The NIO system

NIO is based on two concepts, channels and buffers. Channels are roughly analogous to streams used in the stream model. Buffers do not have a precise analog in the stream model.

The basic streams, InputStream and OutputStream, can read and write bytes; subclasses of these stream classes can read and write other kinds of data. In NIO, all data is read and written via buffers. See Figure 1 for a comparison of the two models.

Figure 1. The stream model uses streams and bytes; the NIO model uses channels and buffers

Also notice that while the stream model distinguishes between InputStreams and OutputStreams, NIO uses one kind of object—a Channel—for both.

The main advantage of buffers is that they deal with data in bulk. You can read and write large blocks of data, and the size of the buffers you use is only limited by the amount of memory you are willing to allocate to them.

Another more subtle advantage of buffers is that they can represent system-level buffers. Some operating systems use a unified memory scheme that allows I/O without copying data from operating system memory into application memory. Some implementations provide Buffer objects that represent these system-level buffers directly, which means you can read and write data with minimal data copying (see Figure 2).

Figure 2. System buffers allow you to use system-level buffers directly, avoiding extraneous data copying

The select facility

The select facility provides an excellent way to deal with a large number of data sources simultaneously. It takes its name from a Unix system call—select()—that provides the same kind of facility to a C program running on a Unix system.

Normally, I/O is done via blocking system calls. When you call a read() method on an input stream, the method blocks until some data is available. If you're reading from a local file, you don't have to wait long for the data to show up. On the other hand, if you are reading from a network file server or a network socket connection, you might wait longer. While you're waiting, your reading thread can't do anything else.

  • Print
  • Feedback

Resources