Asynchronous channel groups
Each asynchronous channel constructed belongs to achannel groupthat shares a pool of Java threads, which are used for handling the completion of initiated asynchronous I/O operations. This might sound like a bit of a cheat, because you could implement most of the asynchronous functionality yourself in Java threads to get the same behaviour, and you'd hope that NIO.2 could be implemented purely using the operating system's asynchronous I/O capabilities for better performance. However, in some cases, it's necessary to use Java threads: for instance, the completion-handler methods are guaranteed to be executed on threads from the pool.
By default, channels constructed with theopen()methods belong to a global channel group that can be configured using the following system variables:
java.nio.channels.DefaultThreadPoolthreadFactory, which defines ajava.util.concurrent.ThreadFactoryto use instead of the default one
java.nio.channels.DefaultThreadPool.initialSize, which specifies the thread pool's initial size
Three utility methods injava.nio.channels.AsynchronousChannelGroupprovide a way to create new channel groups:
withCachedThreadPool()
withFixedThreadPool()
withThreadPool()
These methods take either the definition of the thread pool, given as ajava.util.concurrent.ExecutorService, or ajava.util.concurrent.ThreadFactory. For example, the following call creates a new channel group that has a fixed pool of 10 threads, each of which is constructed with the default thread factory from theExecutorsclass:
AsynchronousChannelGroup tenThreadGroup =
AsynchronousChannelGroup.withFixedThreadPool(10, Executors.defaultThreadFactory());
The three asynchronous network channels have an alternative version of theopen()method that takes a given channel group to use instead of the default one. For example, this call tellschannelto use thetenThreadGroupinstead of the default channel group to obtain threads when required by the asynchronous operations:
AsynchronousServerSocketChannel channel =
AsynchronousServerSocketChannel.open(tenThreadGroup);
Defining your own channel group allows finer control over the threads used to service the operations and also provides mechanisms for shutting down the threads and awaiting termination. Listing 3 shows an example:
Listing 3. Controlling thread shutdown with a channel group
// first initiate a call that won't be satisfied
channel.accept(null, completionHandler);
// once the operation has been set off, the channel group can
// be used to control the shutdown
if (!tenThreadGroup.isShutdown()) {
// once the group is shut down no more channels can be created with it
tenThreadGroup.shutdown();
}
if (!tenThreadGroup.isTerminated()) {
// forcibly shutdown, the channel will be closed and the accept will abort
tenThreadGroup.shutdownNow();
}
// the group should be able to terminate now, wait for a maximum of 10 seconds
tenThreadGroup.awaitTermination(10, TimeUnit.SECONDS);
TheAsynchronousFileChanneldiffers from the other channels in that, in order to use a custom thread pool, theopen()method takes anExecutorServiceinstead of anAsynchronousChannelGroup.
Asynchronous datagram channels and multicasting
The final new channel is theAsynchronousDatagramChannel. It's similar to theAsynchronousSocketChannelbut worth mentioning separately because the NIO.2 API adds support for multicasting to the channel level, whereas in NIO it is only supported at the level of theMulticastDatagramSocket. The functionality is also available injava.nio.channels.DatagramChannelfrom Java 7.
AnAsynchronousDatagramChannelto use as a server can be constructed as follows:
AsynchronousDatagramChannel server = AsynchronousDatagramChannel.open().bind(null);
Next, we set up a client to receive datagrams broadcast to a multicast address. First, we must choose an address in the multicast range (from 224.0.0.0 to and including 239.255.255.255), and also a port that all clients can bind to:
// specify an arbitrary port and address in the range
int port = 5239;
InetAddress group = InetAddress.getByName("226.18.84.25");
We also require a reference to which network interface to use:
// find a NetworkInterface that supports multicasting
NetworkInterface networkInterface = NetworkInterface.getByName("eth0");
Now, we open the datagram channel and set up the options for multicasting, as shown in Listing 4:
Listing 4. Opening a datagram channel and setting multicast options
// the channel should be opened with the appropriate protocol family,
// use the defined channel group or pass in null to use the default channel group
AsynchronousDatagramChannel client =
AsynchronousDatagramChannel.open(StandardProtocolFamily.INET, tenThreadGroup);
// enable binding multiple sockets to the same address
client.setOption(StandardSocketOption.SO_REUSEADDR, true);
// bind to the port
client.bind(new InetSocketAddress(port));
// set the interface for sending datagrams
client.setOption(StandardSocketOption.IP_MULTICAST_IF, networkInterface);
The client can join the multicast group in the following way:
MembershipKey key = client.join(group, networkInterface);
Thejava.util.channels.MembershipKeyis a new class that provides control over the group membership. Using the key you can drop the membership, block and unblock datagrams from certain addresses, and return information about the group and channel.
The server can then send a datagram to the address and port for the client to receive, as shown in Listing 5:
Listing 5. Sending and receiving a datagram
// send message
ByteBuffer message = ByteBuffer.wrap("Hello to all listeners".getBytes());
server.send(message, new InetSocketAddress(group, port));
// receive message
final ByteBuffer buffer = ByteBuffer.allocate(100);
client.receive(buffer, null, new CompletionHandler<SocketAddress, Object>() {
@Override
public void completed(SocketAddress address, Object attachment) {
System.out.println("Message from " + address + ": " +
new String(buffer.array()));
}
@Override
public void failed(Throwable e, Object attachment) {
System.err.println("Error receiving datagram");
e.printStackTrace();
}
});
Multiple clients can also be created on the same port and joined to the multicast group to receive the datagrams sent from the server.
Conclusion
NIO.2's asynchronous channel APIs provide a convenient and standard way of performing asynchronous operations platform-independently. They allow application developers to write programs that use asynchronous I/O in a clear manner, without having to define their own Java threads and, in addition, may give performance improvements by using the asynchronous support on the underlying OS. As with many Java APIs, the amount that the API can exploit an OS's native asynchronous capabilities will depend on the support for that platform.
Part 2: The file system APIs
This article completes our two-part introduction to More New I/O APIs for Java (NIO.2) in Java 7. Like the asynchronous channel APIs explored inPart 1, NIO.2's file system APIs fill some significant gaps in the way previous Java versions handle I/O. According to the NIO.2 Java specification request (JSR 203):
The Java platform has long needed a filesystem interface better than thejava.io.Fileclass. That class does not handle filenames in a way that works consistently across platforms, it does not support efficient file-attribute access, it does not allow sophisticated applications to take advantage of filesystem-specific features (for example, symbolic links) when available, and many of its methods simply return false on error instead of throwing an informative exception.
Coming to the rescue are three new file system packages in the Java 7 beta:
java.nio.file
java.nio.file.attribute
java.nio.file.spi
This article focuses on the most useful classes in these packages:
java.nio.file.Filesandjava.nio.file.FileVisitorallow you to walk through file systems, querying files or directories up to a certain depth and executing user-implemented callback methods for each one found.
java.nio.file.Pathandjava.nio.file.WatchServiceallow you to register to "watch" a specific directory. An application watching directories receives notification if files in those directories are created, modified, or deleted.
java.nio.attribute.*AttributeViewallow you to view file and directory attributes that were previously hidden from Java users. These attributes include file owner and group permissions, access-control lists (ACLs), and extended file attributes.
We'll provide examples that show how to use these classes. The examples are available in a runnable state (seeDownload), and you can try them out on the Java 7 betas available from IBM® and Oracle (both still under development at the time of this writing; seeResources).
File visitors