Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
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
A few obvious additions in Merlin have received most of the press so far, including the XML parser, secure sockets extension,
and 2D graphics enhancements. This article introduces an exciting new API many have overlooked. The new I/O (input/output)
packages finally address Java's long-standing shortcomings in its high-performance, scalable I/O. The new I/O packages --
java.nio.* -- allow Java applications to handle thousands of open connections while delivering scalability and excellent performance.
These packages introduce four key abstractions that work together to solve the problems of traditional Java I/O:
Buffer contains data in a linear sequence for reading or writing. A special buffer provides for memory-mapped file I/O.
charset maps Unicode character strings to and from byte sequences. (Yes, this is Java's third shot at character conversion.)
Channels -- which can be sockets, files, or pipes -- represent a bidirectional communication pipe.
Selectors multiplex asynchronous I/O operations into one or more threads.
Before diving into the new API's gory details, let's review Java I/O's old style. Imagine a basic network daemon. It needs
to listen to a ServerSocket, accept incoming connections, and service each connection. Assume for this example that servicing a connection involves reading
a request and sending a response. That resembles the way a Web server works. Figure 1 depicts the server's lifecycle. At each
heavy black line, the I/O operation blocks -- that is, the operation call won't return until the operation completes.

Figure 1. Blocking points in a typical Java server
Let's take a closer look at each step.
Creating a ServerSocket is easy:
ServerSocket server = new ServerSocket(8001);
Accepting new connections is just as easy, but with a hidden catch:
Socket newConnection = server.accept();
The call to server.accept() blocks until the ServerSocket accepts an incoming network connection. That leaves the calling thread sitting for an indeterminate length of time. If this
application has only one thread, it does a great impression of a system hang.
Once the incoming connection has been accepted, the server can read a request from that socket, as shown in the code below.
Don't worry about the Request object. It is a fiction invented to keep this example simple.
InputStream in = newConnection.getInputStream();
InputStreamReader reader = new InputStreamReader(in);
LineNumberReader lnr = new LineNumberReader(reader);
Request request = new Request();
while(!request.isComplete()) {
String line = lnr.readLine();
request.addLine(line);
}
lnr.readLine() eventually filters down to call SocketInputStream.read(). There, if data waits in the network buffer, the call immediately returns some data to the caller. If there isn't enough
data buffered, then the call to read blocks until enough data is received or the other computer closes the socket. Because
LineNumberReader asks for data in chunks (it extends BufferedReader), it might just sit around waiting to fill a buffer, even though the request is actually complete. The tail end of the request
can sit in a buffer that LineNumberReader has not returned. This code fragment also creates too much garbage, another big problem. LineNumberReader creates a buffer to hold the data it reads from the socket, but it also creates Strings to hold the same data. In fact, internally, it creates a StringBuffer. LineNumberReader reuses its own buffer, which helps a little. Nevertheless, all the Strings quickly become garbage.