[Java]nio(二)——大文件读取、文件加锁

内存映射文件

  内存映射文件允许我们创建和修改那些因为太大而不能放入内存的文件。有了内存映射文件,我们就可以假定整个文件都放在内存中,而且可以完全把它当作非常大的数组来访问。如下代码:

public class LargeMappedFiles {

    static int length = 0x8FFFFFF; // 128MB

    public static void main(String[] args) throws IOException {
        MappedByteBuffer out = new RandomAccessFile("test.data", "rw").getChannel().map(FileChannel.MapMode.READ_WRITE, 0, length);
        for (int i = 0; i < length; i++){
            out.put((byte)'x');
        }
        System.out.println("File writing...");
        for (int i = 0; i < length / 2 + 6; i++){
            System.out.print((char)out.get(i));
        }
    }
}

文件加锁

  文件锁机制允许我们同步访问某个作为共享资源的文件。虽然竞争同一个文件的两个线程可能是存在于不同的Java虚拟机上,或者不同的进程中。文件锁对其他的操作系统进程是可见的,因为Java的文件加锁直接映射到了本地操作系统的加锁工具
  下面代码是对整个文件加锁:

public class FileLocking {
    public static void main(String[] args) throws IOException, InterruptedException {
        FileOutputStream fo = new FileOutputStream("test.data");
        FileLock fl = fo.getChannel().tryLock();
        if (fl != null){
            System.out.println("File is locked");
            TimeUnit.MILLISECONDS.sleep(1000);
            fl.release();
            System.out.println("Release Lock");
        }
        fo.close();
    }
}
  1. 可使用FileChanneltryLock()lock(),就可以获得整个文件的FileLockSocketChannelDatagramChannelServerSocketChannel不需要加锁,因为它们是从单进程实体继承而来;我们通常不在两个进程之间共享网络socket)。
  2. tryLock()lock()的区别:tryLock()是非阻塞式的,它设法获取锁,如果不能获得(其他进程已经持有相同的锁,并且不同享时),它将直接从方法调用返回。lock()则是阻塞式的,它要阻塞进程直至锁可以获得,或调用lock()的线程中断,或调用lock()的通道关闭。
  3. 使用FileLock.release()可以释放锁。
  4. 使用如下方法可以对文件的一部分上锁:
    tryLock(long position, long size, boolean shared)
    或者
    lock(long position, long size, boolean shared)
    加锁的区域由position-size决定,第三个参数指定是否是共享锁
  5. 无参(未指定positionsize)的加锁方法将根据文件尺寸的变化而变化,但是具有固定尺寸的锁不随文件尺寸的变化而变化。
  6. 对独占锁或者共享锁的支持必须由底层的操作系统提供。如果操作系统不支持共享锁并为每一个请求都创建一个锁,那么它就会使用独占锁。
  7. 锁的类型(共享或独占)可以通过FileLock.isShared()进行查询。

对映射文件的部分加锁

  下面代码中有两个线程,分别加锁文件的不同部分

public class LockingMappedFiles {
    static final int LENGTH = 0x8FFFFFF;
    static FileChannel fc;

    public static void main(String[] args) throws IOException {
        fc = new RandomAccessFile("test.data", "rw").getChannel();
        MappedByteBuffer out = fc.map(FileChannel.MapMode.READ_WRITE, 0, LENGTH);
        for (int i = 0; i < LENGTH; i++){
            out.put((byte)'x');
        }
        new LockAndModify(out, 0, 0 + LENGTH / 3);
        new LockAndModify(out, LENGTH / 2, LENGTH / 2 + LENGTH / 4);
    }

    private static class LockAndModify extends Thread{
        private ByteBuffer buff;
        private int start, end;
        LockAndModify(ByteBuffer mbb, int start, int end){
            this.start = start;
            this.end = end;
            mbb.limit(end);
            mbb.position(start);
            buff = mbb.slice(); //lice()方法用于创建一个共享了原始缓冲区子序列的新缓冲区。新缓冲区的position值是0,而其limit和capacity的值都等于原始缓冲区的limit和position的差值。slice()方法将新缓冲区数组的offset值设置为原始缓冲区的position值,然而,在新缓冲区上调用array()方法还是会返回整个数组
            start();
        }

        @Override
        public void run() {
            try{
                FileLock fl = fc.lock(start, end, false);
                System.out.println("Locked:" + start + " to " + end);
                while (buff.position() < buff.limit() - 1){
                    buff.put((byte)(buff.get() + 1));
                }
                fl.release();
                System.out.println("Released: " + start + " to " + end);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

猜你喜欢

转载自blog.csdn.net/vi_nsn/article/details/78956320
今日推荐