Search this site

 Home  I/O  Buffering  Character streams  NIO intro  Buffers  Channels  Buffer performance

Search this site:
Threads Database Profiling Regular expressions Random numbers Compression Exceptions C Equivalents in Java

 What do you think of this article? Did it help you? Found a mistake? Feedback and suggestions here

NIO Channels

In many operating systems and programming languages, a channel is effectively an identifier that represents an open file, network connection or other I/O device. In NIO, an instance of Channel provides methods to read and write to an open file or connection, as well as some other medium-specific functions. Perhaps most significantly, there is generally a special relationship between a Channel and a direct ByteBuffer, allowing data to be transferred to the buffer from the file or connection more efficiently than to a Java byte array or non-direct buffer.

Opening a channel

As with streams, the way to open a channel depends on the medium.

FileChannels

The FileChannel flavour of channel transfers data to and from a file, and also provides some other file-related facilities. An instance of FileChannel is obtained from the getChannel() method of either a RandomAccessFile or a FileInputStream or FileOutputStream.

The FileChannel thus obtained has read() and write() methods which accept a ByteBuffer to receive/provide the data as appropriate. As mentioned, passing a direct ByteBuffer to these methods is likely to provide the most efficient means of transferring data to/from the file. Note also:

  • although FileChannel has both read() and write() methods, which of these will actually work depends on the semantics of the underlying object used to open the file (so e.g. attempting to write() to a channel opened via a FileInputStream will throw an exception);
  • FileChannel provides a position() method for reading/writing the current position in the file at which reading/writing will occur, and slightly paradoxically, these methods will work on a channel opened via a stream;
  • the channel and underlying stream/RandomAccessFile are linked, meaning that:
    • changing the read/write position via the channel changes the next read/write position of methods on the stream/RandomAccessFile (and vice versa);
    • closing the channel closes the stream/RandomAccessFile (and vice versa).

Converting a Channel to a stream (and vice versa)

A ByteBuffer is sometimes a convenient way to work with data in memory, even if when it comes to writing that data to a file or other location, a boring old stream would do the trick. In this case, we simply want the semantics of a Channel— i.e. the ability to pass a ByteBuffer to its read()/write() methods.

The Channels utility class provides various static methods for wrapping a Channel around a stream and vice versa. For example, you can call newChannel() around an InputStream or OutputStream and get an appropriate Channel to read/write to the given stream. In some cases at least, the methods on Channels will actually return an appropriate flavour of Channel that can efficiently access the medium in question (e.g. passing in a FileInputStream will return the selfsame FileChannel that would have been obtained by calling getChannel() on the FileInputStrean). On the other hand, the Channels methods are far from infallible in this respect: for example, pass in a BufferedInputStream wrapped around a FileInputStream, and you won't get back a channel that efficiently reads from the file in the same way that the FileChannel would have. (In some sense, this is arguably the correct behaviour, but it's worth bearing in mind.)

Channel-to-channel transfer

Channels generally provide transferTo() and transferFrom() methods which take another channel as a parameter and allow transfer of data from one channel to another via the most efficient method available. Potentially, this means that data can be transferred, say, from one file to another without any intermediate copying of the data being made in memory.

(Strictly, which of these methods is/are available, and which will function depends on the particular flavour of channel and whether the underlying medium was opened in read or write mode.)

The main caveat when using the channel transfer methods is that there appears to be a not terribly well defined upper limit on the number of bytes that can be transferred in one go. And in any case, you should generally check the return value of transferTo() and transferFrom() to see how many bytes were actually transferred and act accordingly.

comments powered by Disqus

Written by Neil Coffey. Copyright © Javamex UK 2012. All rights reserved.