An NIO. 2 primer–reference

Part 1: The asynchronous channel APIs

The More New I/O APIs for the Java ™ Platform (NIO.2) is one of the major new functional areas in Java 7,adding asynchronous channel functionality and a new file system API to the language. Developers will gain support for platform-independent file operations,asynchronous operations,and multicast socket channels. Part 1 of this two-part article focuses on the asynchronous channel APIs in NIO. 2,and covers the new file system functionality.

An asynchronous channel represents a connection that supports nonblocking operations,such as connecting,reading,and writing,and provides mechanisms for controlling the operations after they've been initiated. The More New I/O APIs for the Java Platform (NIO.2) in Java 7 enhance the New I/O APIs (NIO) introduced in Java 1.4 by adding four asynchronous channels to the java. nio. channels package:

These classes are similar in style to the NIO channel APIs. They share the same method and argument structures,and most operations available to the NIO channel classes are also available in the new asynchronous versions. The main difference is that the new channels enable some operations to be executed asynchronously.

The asynchronous channel APIs provide two mechanisms for monitoring and controlling the initiated asynchronous operations. The first is by returning a java. util. concurrent. Future object,which models a pending operation and can be used to query its state and obtain the result. The second is by passing to the operation an object of a new class, java. nio. channels. CompletionHandler,which defines handler methods that are executed after the operation has completed. Each asynchronous channel class defines duplicate API methods for each operation so that either mechanism can be used.

This article,the first in a on NIO. 2,introduces each of the channels and provides some simple examples to demonstrate their use. The examples are available in a runnable state (see ),and you can try them out on the Java 7 beta releases available from Oracle and IBM ® (both still under development at the time of this writing; see ). In ,you'll learn about the NIO. 2 file system API.

To start,we'll look at the AsynchronousServerSocketChannel and AsynchronousSocketChannel classes. Our first example demonstrates how a simple client/server can be implemented using these new classes. First we'll set up the server.

An AsychronousServerSocketChannel can be opened and bound to an address similarly to a ServerSocketChannel: The bind() method takes a socket address as its argument. A convenient way to find a free port is to pass in a null address,which automatically binds the socket to the local host address and uses a free ephemeral port. Next,we can tell the channel to accept a connection: acceptFuture = server. accept(); This is the first difference from NIO. The accept call always returns immediately,and — unlike ServerSocketChannel. accept(),which returns a SocketChannel — it returns a Future object that can be used to retrieve an AsynchronousSocketChannelat a later time. The generic type of the Future object is the result of the actual operation. For example,a read or write returns aFuture because the operation returns the number of bytes read or written. Using the Future object,the current thread can block to wait for the result: Here it blocks with a timeout of 10 seconds: Or it can poll the current state of the operation,and also cancel the operation: The cancel() method takes a boolean flag to indicate whether the thread performing the accept can be interrupted. This is a useful enhancement; in previous Java releases,blocking I/O operations like this could only be aborted by closing the socket.

Next,we can set up the client by opening and connecting a AsynchronousSocketChannel to the server: Once the client is connected to the server,reads and writes can be performed via the channels using byte buffers,as shown in Listing 1:

// read a message from the client worker. read(readBuffer). get(10,TimeUnit.SECONDS); System. out. println("Message: " + new String(readBuffer.array())); Scattering reads and writes,which take an array of byte buffers,are also supported asynchronously. The APIs of the new asynchronous channels completely abstract away from the underlying sockets: there's no way to obtain the socket directly,whereas previously you could call socket() on,for example,a SocketChannel. Two new methods — getOption and setOption — have been introduced for querying and setting socket options in the asynchronous network channels. For example,the receive buffer size can be retrieved by channel. getOption(StandardSocketOption.SO_RCVBUF) instead of channel. socket(). getReceiveBufferSize();. The alternative mechanism to using Future objects is to register a callback to the asynchronous operation. The CompletionHandler interface has two methods: void completed(V result,A attachment) executes if a task completes with a result of type V. void failed(Throwable e,A attachment) executes if the task fails to complete due to Throwable e. The attachment parameter of both methods is an object that is passed in to the asynchronous operation. It can be used to track which operation finished if the same completion-handler object is used for multiple operations. Let's look at an example using the AsynchronousFileChannel class. We can create a new channel by passing in a java. nio. file. Path object to the static open() method: open commands for FileChannel The format of the open commands for asynchronous channels has been backported to the FileChannelclass. Under NIO,a FileChannel is obtained by calling getChannel() on a FileInputStream,FileOutputStream,or RandomAccessFile. With NIO. 2,a FileChannel can be created directly using anopen() method,as in the examples shown here. Path is a new class in Java 7 that we look at in more detail in . We use thePaths. get(String) utility method to create a Path from a String representing the filename. By default,the file is opened for reading. The open() method can take additional options to specify how the file is opened. For example,this call opens a file for reading and writing,creates it if necessary,and tries to delete it when the channel is closed or when the JVM terminates: An alternative open() method provides finer control over the channel,allowing file attributes to be set.

Next,we want to write to the file and then,once the write has completed,execute something. We first construct a CompletionHandler that encapsulates the "something" as shown in Listing 2:

handler = new CompletionHandler () { @Override public void completed(Integer result,Object attachment) { Sy stem.out.println(attachment + " completed with " + result + " bytes written"); } @ Override public void Failed(Throwable e,Object attachment) { Sy stem.err.println(attachment + " Failed with:"); e.printStackTrace(); } }; Now we can perform the write: The write() method takes: A ByteBuffer containing the contents to write An absolute position in the file An attachment object that is passed on to the completion handler methods A completion handler Operations must give an absolute position in the file to read to or write from. It doesn't make sense for the file to have an internal position marker and for reads/writes to occur from there,because the operations can be initiated before prevIoUs ones are completed and the order they occur in is not guaranteed. For the same reason,there are no methods in the AsynchronousFileChannel API that set or query the position,as there are in FileChannel. In addition to the read and write methods,an asynchronous lock method is also supported,so that a file can be locked for exclusive access without having to block in the current thread (or poll using tryLock) if another thread currently holds the lock. Each asynchronous channel constructed belongs to a channel group that 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 the open() methods belong to a global channel group that can be configured using the following system variables: java. nio. channels. DefaultThreadPoolthreadFactory,which defines a java. util. concurrent. ThreadFactory to use instead of the default one java. nio. channels. DefaultThreadPool. initialSize,which specifies the thread pool's initial size Three utility methods in java. nio. channels. AsynchronousChannelGroup provide a way to create new channel groups: withCachedThreadPool() withFixedThreadPool() withThreadPool() These methods take either the deFinition of the thread pool,given as a java. 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 the Executors class: The three asynchronous network channels have an alternative version of the open() method that takes a given channel group to use instead of the default one. For example,this call tells channel to use the tenThreadGroup instead of the default channel group to obtain threads when required by the asynchronous operations: 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: The AsynchronousFileChannel differs from the other channels in that,in order to use a custom thread pool,the open() method takes anExecutorService instead of an AsynchronousChannelGroup. The final new channel is the AsynchronousDatagramChannel. It's similar to the AsynchronousSocketChannel but 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 in java. nio. channels. DatagramChannel from Java 7. An AsynchronousDatagramChannel to use as a server can be constructed as follows: 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: We also require a reference to which network interface to use: Now,we open the datagram channel and set up the options for multicasting, as shown in Listing 4: The client can join the multicast group in the following way: The java. util. channels. MembershipKey is 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: // receive message final ByteBuffer buffer = ByteBuffer. allocate(100); client. receive(buffer,null,new CompletionHandler
() { @Override public void completed(SocketAddress address,Object attachment) { System.out.println("Message from " + address + ": " + new String(buffer.array())); } @ Override
The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>