Java NIO 之文件通道

 

文件通道总是阻塞式的,因此不能被置于非阻塞模式。现代操作系统都有复杂的缓存和预读取机制,使得本地磁盘 I/O 操作延迟很小。

创建

FileChannel 对象不能直接创建。只能通过正在一个打开的 file 对象 (RandomAccessFile, FileInputStream, FileOutputStream)上调用 getChannel() 方法获取。此方法会返回一个连接到相同文件的 FileChannel 对象并且此 FileChannel 对象具有和 file 对象相同的范根权限。

package java.nio.channels;

public abstract class FileChannel extends AbstractChannel
        implements ByteChannel, GatheringByteChannel, ScatteringByteChannel {

    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 force(boolean metaData);

    public final FileLock lock();

    public abstract FileLook 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 long transferTo(long position, long count, WritableByteChannel target);

    public abstract long transferFrom(ReadableByteChannel src, long position, long count);

}

FileChannel 类本身是抽象的,可以从 getChannel() 方法获取到的实际对象是一个子类的一个实例。

FileChannel 对象是线程安全的。多个进程可以在同一个实例上并发调用方法而不会引起任何问题,不过并非所有的操作都是多线程的,影响通道位置或者影响文件大小的操作都是单线程的。如果有一个线程已经在执行会影响通道位置活文件大小的操作,那么其他尝试进行此类操作的线程必须等待。

访问文件

通底层的文件描述符一样,每个 FileChannel 都有一个叫 file position 的概念。这个值决定文件中哪一出的数据接下来将会被读或者写。从这点上看,FileChannel 类和缓冲区很类似,并且 MappedByeBuffer 类使得我们可以通过 ByteBuffer API 来访问文件数据。

FileChannel 的 position 属性是从底层的文件描述符获得的,该值同时被作为通道引用获取来源的文件对象共享。即一个对象对 position 的更新可以被另一个对象看到:

 public void demoPosition() throws IOException {

        try (RandomAccessFile randomAccessFile = new RandomAccessFile("C:\\works\\temp\\gc.log", "r");
             FileChannel fileChannel = randomAccessFile.getChannel()
        ) {
            // Set the file position.
            randomAccessFile.seek(1000);

            // this will print 1000
            logger.info("file pos: {}", fileChannel.position());

            // change the position using RrandomAccessFile object
            randomAccessFile.seek(500);

            // this will print 500
            logger.info("file pos: {}", fileChannel.position());

            // change the position using FileChannel object.
            fileChannel.position(200L);

            // then it will print 200
            logger.info("file pos: {}", randomAccessFile.getFilePointer());

        }
    }

类似于 ByteBuffer 的 get/put 方法,当字节被 read/write 方法传输时,文件 position 会自动更新。如果 position 值达到了文件大小的值(可以用 size() 方法获得), read() 方法会返回一个文件尾条件值 (-1)。write() 方法会扩展文件的大小写入新数据。

类似于 ByteBuffer,也带有 position 参数的绝对形式的 read/write 方法。这种形式的方法在返回值时不会改变当前文件的 position 。由于通道的状态无需更新,这种绝对的读写可能会更有效率,操作请求可以直接传递到本地代码。多线程可以并发访问同一个文件而不会相互产生干扰。因为每次调用都是原子性的,并不依靠调用之间系统记住的状态。

在文件末尾之外的 position 进行一个绝对读擦做,size() 方法会返回一个 EOF。write() 会导致文件增加以容纳正在被写入的新字节。

当需要减少一个文件的 size 时,truncate() 方法会砍掉新 size 之外的所有数据。小于文件的字节的话,超出新 size 的部分会丢弃;大于文件的字节的话,文件不会被修改。这两种情况下, truncate() 方法都会产生副作用:文件的 position 会被设置为所提供的新 size 的值。

force() 方法则告诉通道强制将全部待定的修改都应用到磁盘的文件上。其参数表示方法返回前文件的元数据是否也要同步的更新到磁盘上。

文件锁定

EOF


Power by TeXt.