1 FileChannel API
FileChannel类可以实现常用的read、write以及scatter/gather操作,同时提供了很多专用于文件的新方法。
文件通道总是阻塞的,因此不能被置于非阻塞模式。对于文件IO,最强大之处在于异步IO,它允许一个进程可以从操作系统请求一个或多个IO操作而不必等待这些操作的完成。
FileChannel对象不能直接创建。一个FileChannel实例只能通过在一个开打的file对象(RandomAccessFile、FileInputStream或FileOutputStream)上调用getChannel()方法获取。FileChannel对象具有与file对象相同的访问权限。
package java.nio.channels;
public abstract class FileChannel extends AbstractChannel
implements ByteChannel, GatheringByteChannel, ScatteringByteChannel
{
// This is a partial API listing
// All methods listed here can throw java.io.IOException
public abstract int read (ByteBuffer dst, long position)
public abstract int write (ByteBuffer src, long position)
public abstract long size( )
public abstract long position( )
public abstract void position (long newPosition)
public abstract void truncate (long size)
public abstract void force (boolean metaData)
public final FileLock lock( )
public abstract FileLock lock (long position, long size, boolean shared)
public final FileLock tryLock( )
public abstract FileLock tryLock (long position, long size, boolean shared)
public abstract MappedByteBuffer map (MapMode mode, long position, long size)
public static class MapMode
{
public static final MapMode READ_ONLY
public static final MapMode READ_WRITE
public static final MapMode PRIVATE
}
public abstract lo ng transferTo (long position, long count, WritableByteChannel target)
public abstract long transferFrom (ReadableByteChannel src,
long position, long count)
}
FileChannel对象是线程安全的。多个进程可以同一个实例上并发调用方法而不会引起任何问题,但并非所有的操作都是多线程的。影响通道位置或者影响文件大小的操作都是单线程的。如果一个线程已经在执行会影响通道位置或文件大小的操作,其他尝试进行此类操作之一的线程必须等待。
2 访问文件
每个FileChannel对象都同一个文件描述符(file descriptor)有一对一的关系。
同底层的文件描述符一样,每个FileChannel都有一个叫'file position'的概念,该position值决定文件中哪一处的数据接下来被读或写。MappedByteBuffer类使得我们可以通过ByteBuffer API来访问数据文件。
第二种形式position()方法带Long参数将通道的position设置为指定值。不可以把position设置到超出文件尾,这样会把position设置为指定值而不改变文件大小(当磁盘上一个文件的分配空间小于它的文件大小时会出现“文件空洞”)。
FileChannel 位置(position)是从底层的文件描述符获得的,该position同时被作为通道引用获。
类似于缓冲区的get( ) 和put( )方法,当字节被read( ) 或write( ) 方法传输时,文件position会自动更新。如果position值达到了文件大小的值(文件大小的值可以通过size( )方法返回),read( ) 方法会返回一个文件尾条件值(-1)。可是,不同于缓冲区的是,如果实现write( ) 方法时position前进到超过文件大小的值,该文件会扩展以容纳新写入的字节。
同样类似于缓冲区,也有带position参数的绝对形式的read( ) 和 write( ) 方法。这种绝对形式的方法在返回值时不会改变当前的文件position。
当需要减少一个文件的size 时,truncate( )方法会砍掉您所指定的新size 值之外的所有数据。
force( ) ,该方法告诉通道强制将全部待定的修改都应用到磁盘的文件上。
force( ) 方法的布尔型参数表示在方法返回值前文件的元数据(metadata)是否也要被同步更新到磁盘。元数据指文件所有者、访问权限、最后一次修改时间等信息。
3 文件锁定
锁(lock)可以使共享的或独占的。并非所有的操作系统和文件都支持共享文件锁。
FileChannel实现的文件锁定模型的一个重要注意项:锁的对象是文件而不是通道或者线程。
如果一个线程在某文件上获得一个独占锁,然后第二个线程利用一个单独打开的通道来请求该文件,那么第二个线程的请求会被批准,但如果这两个线程运行在不同的JVM上,第二个线程会阻塞,因为锁最终是由操作系统或文件系统来判优的,并且几乎总是在进程级而非线程级上判优。如果需要控制多个线程的并发访问,内存映射文件可能是一个合适的选择。
public abstract class FileChannel extends AbstractChannel
implements ByteChannel, GatheringByteChannel, ScatteringByteChannel
{
// This is a partial API listing
public final FileLock lock( )
public abstract FileLock lock (long position, long size,boolean shared)
public final FileLock tryLock( )
public abstract FileLock tryLock (long position, long size,boolean shared)
}
锁是在文件内部区域上获得的,调用带参数的Lock( )方法会指定文件内部锁定区域的开始position 以及锁定区域的size。第三个参数shared表示获取的锁是共享的还是独占的。
要获得一个共享锁,您必须先以只读权限打开文件,而请求独占锁时则需要写权限。
锁定区域的范围不一定要限制在文件的size值以内,锁可以扩展从而超出文件尾。因此,我们可以提前把待写入数据的区域锁定,我们也可以锁定一个不包含任何文件内容的区域。不带参数的简单形式的lock()方法是一种在整个文件上请求独占锁的便捷方法,锁定区域等于它能到达的最大范围。
fileChannel.lock (0L, Long.MAX_VALUE, false);
tryLock和lock方法相同,但如果请求的锁不能立即获取到则返回一个Null。
public abstract class FileLock
{
public final FileChannel channel( )
public final long position( )
public final long size( )
public final boolean isShared( )
public final boolean overlaps (long position, long size)
public abstract boolean isValid( );
public abstract void release( ) throws IOException;
}
FileLock类封装一个锁定的文件区域。Fi leLock 对象由FileChannel 创建并且总是关联到那个特定的通道实例。
一个FileLock对象创建之后即有效,直到它的release( ) 方法被调用或它所关联的通道被关闭或Java 虚拟机关闭时才会失效。锁的有效性可能会随着时间而改变,不过它的其他属性位置(position)、范围大小(size )