JAVA NIO 文件锁FileLock

文件锁可以是shared(共享锁)或者exclusive(排他锁)。不是所有的平台都以同一种方式实现文件锁,不同的操作系统可能不同,同一操作系统上的不同文件系统也可能不同。有些操作系统只提供协同锁,有些只提供强制锁,有些则都提供。

文件锁是以文件为单位的,不是以通道,也不是线程。所以文件锁不适合同一个多个线程访问的情形。如果一个线程获得了给定文件的排他锁,第二个线程请求打开了一个新的channel,请求获得排他锁,请求会被批准。但如果这两个线程运行在不同的JVM中,第二个线程会阻塞,因为锁往往是根据进程来进行裁决,而不是线程。锁工作于一个文件,而不是单独的文件处理器或是通道。
/*
如果你需要控制多个线程之间的同步,你可能需要实现自己的轻量级的锁,内存映射文件可能是个适合的选择
*/

public abstract class FileChannel extends AbstractChannel implements ByteChannel, GatheringByteChannel, ScatteringByteChannel {
  
  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大小,第三个布尔参数代表是锁是否共享。锁的区域并不受到文件大小的限制,锁可以超过文件的大小,也就是说在一段区域被写入数据之前锁住,是可行的。相反的,如果文件的大小超出了锁的限制,也就将不受到锁的限制。不带参数的lock方法,等效于
fileChannel.lock(0L,Long.MAX_VALUE, false);
如果你的请求是有效的,那么lock方法就会生效,但是要等待前一个锁(如果存在的话)释放。

tryLock方法是lock方法非阻塞的变种,功能和lock相似,但是如果不能立刻获得锁的话,tryLock会返回null。从创建开始,直到调用FileLock的release方法,FileLock对象都是有效的。可以通过isValid方法测试。一个锁是否有效可能会改变,但锁的位置,大小,是否共享,是不变的。

你可以通过isShared判断锁是否为共享锁,如果内在的文件系统操作系统不支持共享,那么这个方法总是会返回false,就算你传递true作为构造函数也一样。FileLock是线程安全的,多个线程可以通过一个FileLock进行操作。尽管FileLock对象和一个Channel相关,但是其实锁是和内在的文件联系的。这有可能造成冲突,也有可能死锁,如果你完成了操作而没有释放锁的话。一个典型的代码如下所示:

FileLock lock = fileChannel.lock();
try{
  <perform read/write/whatever on channel>
} catch (IOException e) {
  <handle unexcepted exception>
} finally {
  lock.release();
}

下面是一个使用FileLock进行操作的例子


    private static final int SIZEOF_INT = 4;
    private static final int INDEX_START = 0;
    private static final int INDEX_COUNT = 10;
    private static final int INDEX_SIZE = INDEX_COUNT * SIZEOF_INT;

    private ByteBuffer buffer = ByteBuffer.allocate(INDEX_SIZE);
    private IntBuffer indexBuffer = buffer.asIntBuffer();
    private Random rand = new Random();

    public static void main(String[] args) throws Exception{
        boolean writer = false;
        String filename;
        //决定你所做的操作,读或者写
        if(args.length!=2) {
            System.out.println("Usage: [-r|-w] filename");
            return;
        }
        writer = args[0].equals("-w");//true写false读
        filename = args[1];
        RandomAccessFile raf = new RandomAccessFile(filename,writer?"rw":"r");
        FileChannel fc = raf.getChannel();//通过RandomAccessFile拿到fileChannel
        LockTest lockTest = new LockTest();
        if(writer) {
            lockTest.doUpdates(fc);
        } else {
            lockTest.doQueries(fc);
        }
    }

    void doQueries (FileChannel fc) throws Exception {
        //如果是单次操作的话,没有这个循环,这里使用这个循环,为了多次
        //运行程序,发现锁的工作原理
        while (true) {
            FileLock lock = fc.lock(INDEX_START,INDEX_SIZE,true);
            int reps = rand.nextInt(60) + 20;
            for(int i=0; i<reps; i++) {
                int n = rand.nextInt(INDEX_COUNT);
                int position = INDEX_START + (n*SIZEOF_INT);
                buffer.clear();
                fc.read(buffer,position);
                int value = indexBuffer.get(n);
                Thread.sleep(100);//doing some work
            }
            lock.release();
            Thread.sleep(rand.nextInt(3000)+500);
        }
    }

    void doUpdates (FileChannel fc) throws Exception {
        while (true) {
            FileLock lock = fc.lock(INDEX_START,INDEX_SIZE,false);
            updateIndex(fc);
            lock.release();
            Thread.sleep(rand.nextInt(2000)+500);
        }
    }

    private int idxval = 1;

    private void updateIndex (FileChannel fc) throws Exception{
        indexBuffer.clear();
        for(int i=0; i<INDEX_COUNT; i++) {
            idxval++;
            indexBuffer.put(idxval);
            Thread.sleep(500);
        }
        buffer.clear();
        fc.write(buffer,INDEX_START);
    }


}
    原文作者:ShootHzj
    原文地址: https://www.jianshu.com/p/4efdcd49db22
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞