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包含如下特性:
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 OperationsMethod Time
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
FileInputStream,
FileOutputStream, 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种映射模式:
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
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值,使用如下的循环
NOTE
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.java1. 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
java.io.FileOutputStream 1.0
java.io.RandomAccessFile 1.0
java.nio.channels.FileChannel 1.4
java.nio.Buffer 1.4
java.nio.ByteBuffer 1.4
The Buffer Data StructureWhen 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 ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, and ShortBuffer. In practice, you will most commonly use ByteBuffer and CharBuffer. As shown in Figure 12-13, a buffer has
These values fulfill the condition
Figure 12-13. A bufferThe 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
java.nio.CharBuffer 1.4
File LockingConsider 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
Keep in mind that file locking is system dependent. Here are some points to watch for:
java.nio.channels.FileChannel 1.4
java.nio.channels.FileLock 1.4
|