java.nio简介

 
  Previous section Next section


New I/O

 
JDK 1.4 contains a number of features for improved input/output processing, collectively called the "new I/O," in the  java.nio package. (Of course, the "new" moniker is somewhat regrettable because, a few years down the road, the package won't be new any longer.)
JDK1.4包含了很多增强的I/O处理机制,归总为new I/O.在java.nio包下.(很显然,再过几年,"new"就不再new了,所以用new这个名称很不合适).
 
The package includes support for the following features:
NIO包含如下特性:
 
  • Memory-mapped files

  • File locking

  • Character set encoders and decoders

  • Nonblocking I/O
    文件的内存映射
    文件锁机制
    字符集支持,编解码
    非阻塞I/O
 
We already introduced  character sets on page  634. In this section, we discuss only the first two features. Nonblocking I/O requires the use of threads, which are covered in Volume 2.
字符集在前面章节已经讲过,本章只前两个特性.非阻塞I/O需要用到thread,thread在卷2中讲解.
 

Memory-Mapped Files

 
Most operating systems can take advantage of the virtual memory implementation to "map" a file, or a region of a file, into memory. Then the file can be accessed as if it were an in-memory array, which is much faster than the traditional file operations.
大多数操作系统都可以利用虚拟内存将文件或文件的一部分映射到内存中.
这样文件的存取就好像从memory的数组中读取一样,会比传统方式要快速得多.
 
At the end of this section, you can find a program that computes the CRC32 checksum of a file, using traditional file input and a memory-mapped file. On one machine, we got the timing data shown in  Table 12-7 when computing the checksum of the 37-Mbyte file  rt.jar in the  jre/lib directory of the JDK.
如下部分的程序代码可以计算file的CRC32校验值,通过传统的文件读取或从内存中映射的文件读取都可以.实例代码计算37-Mbyte的rt.jar的CRC32校验值所消耗的时间见下边表格12-7:
 
 
Table 12-7. Timing Data for File Operations

Method

Time

Plain Input Stream

110 seconds

Buffered Input Stream

9.9 seconds

Random Access File

162 seconds

Memory Mapped file

7.2 seconds


 
As you can see, on this particular machine, memory mapping is a bit faster than using buffered sequential input and dramatically faster than using a  RandomAccessFile.
如你所见,在同一电脑上,从内存映射的文件中读取会比从buffer的输入流中读取
 
Of course, the exact values will differ greatly from one machine to another, but it is obvious that the performance gain can be substantial if you need to use random access. For sequential reading of files of moderate size, on the other hand, there is no reason to use memory mapping.
当然,实际值会因电脑不同而不同,很明显如果你需要用随机读取的话,性能增益会更大.
对于一般大小的文件的顺序读取,是没必要使用内存映射的.
 
The  java.nio package makes memory mapping quite simple. Here is what you do.
java.nio包使得内存映射变得相当简单,如下:
 
First, get a channel from the file. A channel is an abstraction for disk files that lets you access operating system features such as memory mapping, file locking, and fast data transfers between files. You get a channel by calling the  getChannel method that has been added to the  FileInputStreamFileOutputStream, and  RandomAccessFile class.
首先,从file获取channel.channel是对磁盘文件的抽象,通过channel可以访问操作系统的属性:如内存映像,文件锁,文件间的高速数据传输.
通过getChannel方法可以获得channel,该方法已经被加到FileInputStream中, FileOutputStream和 RandomAccessFile 类中.
 
FileInputStream in = new FileInputStream(. . .);
FileChannel channel = in.getChannel();

 
Then you get a  MappedByteBuffer from the channel by calling the  map method of the  FileChannel class. You specify the area of the file that you want to map and a mapping mode. Three modes are supported:
FileChannel 类的方法 map返回 MappedByteBuffer 对象.你可以执行要映射的文件的area和映射模式.支持3种映射模式:
 
  • FileChannel.MapMode.READ_ONLY: The resulting buffer is read-only. Any attempt to write to the buffer results in a  ReadOnlyBufferException.
    返回的buffer是只读的.尝试往buffer中写数据会导致 ReadOnlyBufferException
  • FileChannel.MapMode.READ_WRITE: The resulting buffer is writable, and the changes will be written back to the file at some time. Note that other programs that have mapped the same file may not see those changes immediately. The exact behavior of simultaneous file mapping by multiple programs is operating-system dependent.
    返回的buffer是可写的,改变的数据会在某个时候写回到文件中.注意:如果有别的程序映射的是同一个文件,别的程序不能立即看到发生的变化.
    多个程序同时映射同一个文件,具体这样的行为结果是什么依赖于操作系统
  • FileChannel.MapMode.PRIVATE: The resulting buffer is writable, but any changes are private to this buffer and are not propagated to the file.
    返回的buffer是可写的,但是任何的改变都是对该buffer private的,所做的更改不会对文件造成影响.
 
Once you have the buffer, you can read and write data, using the methods of the  ByteBuffer class and the  Buffer superclass.
一旦你获取buffer对象,你就可以使用 ByteBuffer 和 Buffer 的父类中的方法读写数据.
 
Buffers support both sequential and random data access. A buffer has a position that is advanced by  get and  put operations. For example, you can sequentially traverse all bytes in the buffer as
Buffer支持顺序和随机数据访问.buffer 可以通过get 和put方法操作,你可以顺序遍历buffer中的所有bytes,如下:
 
while (buffer.hasRemaining())
{
   byte b = buffer.get();
   . . .
}

 
Alternatively, you can use random access:
相应的,你也可以使用随机访问方式,如下:
 
for (int i = 0; i < buffer.limit(); i++)
{
   byte b = buffer.get(i);
   . . .
}

 
You can also read and write arrays of bytes with the methods
你可以通过如下方法读写byte数组
 
get(byte[] bytes)
get(byte[], int offset, int length)

Finally, there are methods

getInt
getLong
getShort
getChar
getFloat
getDouble

 
to read primitive type values that are stored as binary values in the file. As we already mentioned, Java uses big-endian ordering for binary data. However, if you need to process a file containing binary numbers in little-endian order, simply call
还可以通过getInt读取以二进制方式存储在文件中的基本类型的值.前面我们提过,java中的二进制存储使用的是big-endian ordering,然而如果你需要处理的文件是little-endian order的,你可以通过调用如下方法:
 
buffer.order(ByteOrder.LITTLE_ENDIAN);

 
To find out the current byte order of a buffer, call
 
想看当前buffer的byte order可以调用buffer.order()
 
ByteOrder b = buffer.order()

CAUTION

This pair of methods does not use the set/get naming convention. 如上方法:getLong和puLong不遵循 set/get的命名规范.


 
To write numbers to a buffer, use one of the methods
往buffer中写数字的话,可以用如下方法:
 
putInt
putLong
putShort
putChar
putFloat
putDouble

 
Example 12-8 computes the 32-bit cyclic redundancy checksum (CRC32) of a file. That quantity is a checksum that is often used to determine whether a file has been corrupted. Corruption of a file makes it very likely that the checksum has changed. The  java.util.zip package contains a class  CRC32 that computes the checksum of a sequence of bytes, using the following loop:
示例代码会计算文件的CRC32校验值.CRC32校验值多用于检查文件是否损坏.文件损坏时一般会导致CRC32的值改变. java.util.zip有个 CRC32 类用来计算一组byte的CRC32值,使用如下的循环


CRC32 crc = new CRC32();
while (more bytes)
crc.update(next byte)
long checksum = crc.getValue();

NOTE

For a nice explanation of the CRC algorithm, see  http://www.relisoft.com/Science/CrcMath.html.
关于CRC算法的更详细解释可参阅...

The details of the CRC computation are not important. We just use it as an example of a useful file operation.

Run the program as
 

java NIOTest filename
CRC算法的细节不是很重要,我们只是用它在示例代码中做文件操作.
 
Example 12-8. NIOTest.java
  1. import java.io.*;
  2. import java.nio.*;
  3. import java.nio.channels.*;
  4. import java.util.zip.*;
  5.
  6. /**
  7.    This program computes the CRC checksum of a file.
  8.    Usage: java NIOTest filename
  9. */
 10. public class NIOTest
 11. {
 12.    public static long checksumInputStream(String filename)
 13.       throws IOException
 14.    {
 15.       InputStream in = new FileInputStream(filename);
 16.       CRC32 crc = new CRC32();
 17.
 18.       int c;
 19.       while((c = in.read()) != -1)
 20.          crc.update(c);
 21.       return crc.getValue();
 22.    }
 23.
 24.    public static long checksumBufferedInputStream(String filename)
 25.       throws IOException
 26.    {
 27.       InputStream in = new BufferedInputStream(new FileInputStream(filename));
 28.       CRC32 crc = new CRC32();
 29.
 30.       int c;
 31.       while((c = in.read()) != -1)
 32.          crc.update(c);
 33.       return crc.getValue();
 34.    }
 35.
 36.    public static long checksumRandomAccessFile(String filename)
 37.       throws IOException
 38.    {
 39.       RandomAccessFile file = new RandomAccessFile(filename, "r");
 40.       long length = file.length();
 41.       CRC32 crc = new CRC32();
 42.
 43.       for (long p = 0; p < length; p++)
 44.       {
 45.          file.seek(p);
 46.          int c = file.readByte();
 47.          crc.update(c);
 48.       }
 49.       return crc.getValue();
 50.    }
 51.
 52.    public static long checksumMappedFile(String filename)
 53.       throws IOException
 54.    {
 55.       FileInputStream in = new FileInputStream(filename);
 56.       FileChannel channel = in.getChannel();
 57.
 58.       CRC32 crc = new CRC32();
 59.       int length = (int) channel.size();
 60.       MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, length);
 61.
 62.       for (int p = 0; p < length; p++)
 63.       {
 64.          int c = buffer.get(p);
 65.          crc.update(c);
 66.       }
 67.       return crc.getValue();
 68.    }
 69.
 70.    public static void main(String[] args)
 71.       throws IOException
 72.    {
 73.       System.out.println("Input Stream:");
 74.       long start = System.currentTimeMillis();
 75.       long crcValue = checksumInputStream(args[0]);
 76.       long end = System.currentTimeMillis();
 77.       System.out.println(Long.toHexString(crcValue));
 78.       System.out.println((end - start) + " milliseconds");
 79.
 80.       System.out.println("Buffered Input Stream:");
 81.       start = System.currentTimeMillis();
 82.       crcValue = checksumBufferedInputStream(args[0]);
 83.       end = System.currentTimeMillis();
 84.       System.out.println(Long.toHexString(crcValue));
 85.       System.out.println((end - start) + " milliseconds");
 86.
 87.       System.out.println("Random Access File:");
 88.       start = System.currentTimeMillis();
 89.       crcValue = checksumRandomAccessFile(args[0]);
 90.       end = System.currentTimeMillis();
 91.       System.out.println(Long.toHexString(crcValue));
 92.       System.out.println((end - start) + " milliseconds");
 93.
 94.       System.out.println("Mapped File:");
 95.       start = System.currentTimeMillis();
 96.       crcValue = checksumMappedFile(args[0]);
 97.       end = System.currentTimeMillis();
 98.       System.out.println(Long.toHexString(crcValue));
 99.       System.out.println((end - start) + " milliseconds");
100.    }
101. }


java.io.FileInputStream 1.0

  • FileChannel getChannel()3 1.4
    获取Channel

    returns a channel for accessing this stream.


java.io.FileOutputStream 1.0

  • FileChannel getChannel() 1.4
    获取Channel

    returns a channel for accessing this stream.


java.io.RandomAccessFile 1.0

  • FileChannel getChannel() 1.4
    获取Channel

    returns a channel for accessing this file.


java.nio.channels.FileChannel 1.4

  • MappedByteBuffer map(FileChannel.MapMode mode, long position, long size)

    maps a region of the file to memory.

    Parameters:

    mode

    One of the constants READ_ONLY, READ_WRITE, or PRIVATE in the FileChannel.MapMode class

     

    position

    The start of the mapped region开始索引位置

     

    size

    The size of the mapped region 要映射的文件部分的大小/长度


java.nio.Buffer 1.4

  • boolean hasRemaining()

    returns  TRue if the current buffer position has not yet reached the buffer's limit position.
    如果position的值还没有达到buffer的limit值,返回true
  • int limit()

    returns the limit position of the buffer, that is, the first position at which no more values are available.
    buffer的极限位置

java.nio.ByteBuffer 1.4

  • byte get()

    gets a byte from the current position and advances the current position to the next byte.
    读取当前position的
  • byte get(int index)

    gets a byte from the specified index.

  • ByteBuffer put(byte b)

    puts a byte to the current position and advances the current position to the next byte. Returns a reference to this buffer.
    放入一个byte到当前索引位置,并把position移动到下一byte上,返回对当前buffer的引用.
  • ByteBuffer put(int index, byte b)

    puts a byte at the specified index. Returns a reference to this buffer.

  • ByteBuffer get(byte[] destination)

  • ByteBuffer get(byte[] destination, int offset, int length)

    fill a byte array, or a region of a byte array, with bytes from the buffer, and advance the current position by the number of bytes read. If not enough bytes remain in the buffer, then no bytes are read, and a  BufferUnderflowException is thrown. Return a reference to this buffer.
    往buffer中填充byte数组或byte数组的一部分,并把position定位到读取的byte个数位置上.如果buffer中没有足够的byte用于存放,一个byte也不读取,抛出 BufferUnderflowException.返回对该buffer的引用.
     
     

    Parameters:

    destination

    The byte array to be filled

     

    offset

    The offset of the region to be filled

     

    length

    The length of the region to be filled

  • ByteBuffer put(byte[] source)

  • ByteBuffer put(byte[] source, int offset, int length)

    put all bytes from a byte array, or the bytes from a region of a byte array, into the buffer, and advance the current position by the number of bytes read. If not enough bytes remain in the buffer, then no bytes are written, and a  BufferOverflowException is thrown. Returns a reference to this buffer.
     
     

    Parameters:

    source

    The byte array to be written

     

    offset

    The offset of the region to be written

     

    length

    The length of the region to be written

  • Xxx getXxx()

  • Xxx getXxx(int index)

  • ByteBuffer putXxx(xxx value)

  • ByteBuffer putXxx(int index, xxx value)

    are used for relative and absolute reading and writing of binary numbers. Xxx is one of IntLongShortCharFloat, or Double.

  • ByteBuffer order(ByteOrder order)

  • ByteOrder order()

    set or get the byte order. The value for  order is one of the constants  BIG_ENDIAN or  LITTLE_ENDIAN of the  ByteOrder class.
    set buffer的big-endian order 或little-endian order.

The Buffer Data Structure

When you use memory mapping, you make a single buffer that spans the entire file, or the area of the file in which you are interested. You can also use buffers to read and write more modest chunks of information.

In this section, we briefly describe the basic operations on Buffer objects. A buffer is an array of values of the same type. The Buffer class is an abstract class with concrete subclasses ByteBufferCharBufferDoubleBufferFloatBufferIntBufferLongBuffer, and ShortBuffer. In practice, you will most commonly use ByteBuffer and CharBuffer. As shown in Figure 12-13, a buffer has

  • a capacity that never changes

  • a position at which the next value is read or written

  • a limit beyond which reading and writing is meaningless

  • optionally, a mark for repeating a read or write operation

These values fulfill the condition

0

mark

position

limit

capacity


Figure 12-13. A buffer


The principal purpose for a buffer is a "write, then read" cycle. At the outset, the buffer's position is 0 and the limit is the capacity. Keep calling put to add values to the buffer. When you run out of data or you reach the capacity, it is time to switch to reading.

Call flip to set the limit to the current position and the position to 0. Now keep calling get while the remaining method (which returns limit - position) is positive. When you have read all values in the buffer, call clear to prepare the buffer for the next writing cycle. The clear method resets the position to 0 and the limit to the capacity.

If you want to re-read the buffer, use rewind or mark/reset—see the API notes for details.


java.nio.Buffer 1.4

  • Buffer clear()

    prepares this buffer for writing by setting the position to zero and the limit to the capacity; returns this.

  • Buffer flip()

    prepares this buffer for reading by setting the limit to the position and the position to zero; returns this.

  • Buffer rewind()

    prepares this buffer for re-reading the same values by setting the position to zero and leaving the limit unchanged; returns this.

  • Buffer mark()

    sets the mark of this buffer to the position; returns this.

  • Buffer reset()

    sets the position of this buffer to the mark, thus allowing the marked portion to be read or written again; returns this.

  • int remaining()

    returns the remaining number of readable or writable values, that is, the difference between limit and position.

  • int position()

    returns the position of this buffer.

  • int capacity()

    returns the capacity of this buffer.


java.nio.CharBuffer 1.4

  • char get()

  • CharBuffer get(char[] destination)

  • CharBuffer get(char[] destination, int offset, int length)

    gets one char value, or a range of char values, starting at the buffer's position and moving the position past the characters that were read. The last two methods return this.

  • CharBuffer put(char c)

  • CharBuffer put(char[] source)

  • CharBuffer put(char[] source, int offset, int length)

  • CharBuffer put(String source)

  • CharBuffer put(CharBuffer source)

    puts one char value, or a range of char values, starting at the buffer's position and advancing the position past the characters that were written. When reading from a CharBuffer, all remaining characters are read. All methods return this.

  • CharBuffer read(CharBuffer destination)

    gets char values from this buffer and puts them into the destination until the destination's limit is reached. Returns this.

File Locking

Consider a situation in which multiple simultaneously executing programs need to modify the same file. Clearly, the programs need to communicate in some way, or the file can easily become damaged.

File locks control access to a file or a range of bytes within a file. However, file locking varies greatly among operating systems, which explains why file locking capabilities were absent from prior versions of the JDK.

Frankly, file locking is not all that common in application programs. Many applications use a database for data storage, and the database has mechanisms for resolving concurrent access problems. If you store information in flat files and are worried about concurrent access, you may well find it simpler to start using a database rather than designing complex file locking schemes.

Still, there are situations in which file locking is essential. Suppose your application saves a configuration file with user preferences. If a user invokes two instances of the application, it could happen that both of them want to write the configuration file at the same time. In that situation, the first instance should lock the file. When the second instance finds the file locked, it can decide to wait until the file is unlocked or simply skip the writing process.

To lock a file, call either the lock or tryLock method of the FileChannel class:

FileLock lock = channel.lock();

or

FileLock lock = channel.tryLock();

The first call blocks until the lock becomes available. The second call returns immediately, either with the lock or null if the lock is not available. The file remains locked until the channel is closed or the release method is invoked on the lock.

You can also lock a portion of the file with the call

FileLock lock(long start, long size, boolean exclusive)

or

FileLock tryLock(long start, long size, boolean exclusive)

The exclusive flag is TRue to lock the file for both reading and writing. It is false for a shared lock, which allows multiple processes to read from the file, while preventing any process

from acquiring an exclusive lock. Not all operating systems support shared locks. You may get an exclusive lock even if you just asked for a shared one. Call the isShared method of the FileLock class to find out which kind you have.

NOTE

If you lock the tail portion of a file and the file subsequently grows beyond the locked portion, the additional area is not locked. To lock all bytes, use a size of Long.MAX_VALUE.


Keep in mind that file locking is system dependent. Here are some points to watch for:

  • On some systems, file locking is merely advisory. If an application fails to get a lock, it may still write to a file that another application has currently locked.

  • On some systems, you cannot simultaneously lock a file and map it into memory.

  • File locks are held by the entire Java virtual machine. If two programs are launched by the same virtual machine (such as an applet or application launcher), then they can't each acquire a lock on the same file. The lock and TRyLock methods will throw an OverlappingFileLockException if the virtual machine already holds another overlapping lock on the same file.

  • On some systems, closing a channel releases all locks on the underlying file held by the Java virtual machine. You should therefore avoid multiple channels on the same locked file.

  • Locking files on a networked file system is highly system dependent and should probably be avoided.


java.nio.channels.FileChannel 1.4

  • FileLock lock()

    acquires an exclusive lock on the entire file. This method blocks until the lock is acquired.

  • FileLock tryLock()

    acquires an exclusive lock on the entire file, or returns null if the lock cannot be acquired.

  • FileLock lock(long position, long size, boolean shared)

  • FileLock tryLock(long position, long size, boolean shared)

    acquire a lock on a region of the file. The first method blocks until the lock is acquired, and the second method returns null if the lock cannot be acquired.

    Parameters:

    position

    The start of the region to be locked

     

    size

    The size of the region to be locked

     

    shared

    true for a shared lock, false for an exclusive lock


java.nio.channels.FileLock 1.4

  • void release()

    releases this lock.


 
  Previous section Next section
Top

你可能感兴趣的:(java)