多进程文件锁 FileLock 相关实战场景题目完全解析

  • 问:说说你对 FileLock 进程锁的认识?最好能举例说明你知道的各种场景?

  • 答:这是一个很具有发挥空间的题。FileLock 是进程文件锁,用于进程间并发,控制不同程序(JVM)对同一文件的并发访问,是 JDK 1.4 提供的一个类,可以通过对一个可写文件加锁,保证同时只有一个进程可以拿到文件锁,这个进程从而可以对文件进行操作,而其它拿不到锁的进程要么被挂起等待,要么可以去做一些其它事情,这种机制保证了进程间文件的并发安全操作。

      而 FileLock 的使用核心离不开对独占锁和共享锁的理解。独占锁也叫排它锁,如果一个任务获得一个文件的独占锁,那么其它任务就不能再获得同一文件的独占锁或共享锁,直到独占锁被释放。共享锁指的是如果一个任务获得一个文件的共享锁,那么其它任务可以获得同一文件的共享锁或同一文件部分内容的共享锁,但不能获得独占锁,共享锁情况下是不支持写操作的。

      此外 FileLock 文件锁的效果是与操作系统相关的,是由操作系统底层来实现(如在 Win 的进程间不能同时读写一个文件,而在 Linux 的不同进程可以同时读写一个文件)。

所以基于此可以通过控制变量法总结出如下几种伪代码解析。

//进程1代码片段
RandomAccessFile randomAccessFile = new RandomAccessFile("test.txt", "rw");
FileChannel channel = randomAccessFile.getChannel();
FileLock lock = channel.lock();
for (int i = 0; i < 10; i++) {
    randomAccessFile.writeChars("1");
    Thread.sleep(1000);
}
lock.release();
randomAccessFile.close();
channel.close();

////进程2代码片段
RandomAccessFile randomAccessFile = new RandomAccessFile("test.txt", "rw");
FileChannel channel = randomAccessFile.getChannel();
FileLock lock = channel.lock();
randomAccessFile.length();
lock.release();
randomAccessFile.close();
channel.close();

//如上程序先运行进程1,然后同时启动进程2。
//会发现进程2会等到进程1执行完lock.release()后才开始执行channel.lock()之后的代码,
//这期间进程2会一直阻塞挂起等待进程1释放锁。
//此时进程1,2使用的都是独占锁,这是进程并发写操作最常用的一种手段。
//进程1代码片段
RandomAccessFile randomAccessFile = new RandomAccessFile("test.txt", "rw");
FileChannel channel = randomAccessFile.getChannel();
FileLock lock = channel.lock();
for (int i = 0; i < 10; i++) {
    randomAccessFile.writeChars("1");
    Thread.sleep(1000);
}
lock.release();
randomAccessFile.close();
channel.close();

////进程2代码片段
RandomAccessFile randomAccessFile = new RandomAccessFile("test.txt", "rw");
FileChannel channel = randomAccessFile.getChannel();
FileLock lock = channel.lock(0, Integer.MAX_VALUE, true);
randomAccessFile.length();
lock.release();
randomAccessFile.close();
channel.close();

//如上程序先运行进程1,然后同时启动进程2。
//会发现进程2会等到进程1执行完lock.release()后才开始执行channel.lock(0, Integer.MAX_VALUE, true)之后的代码,
//这期间进程2会一直阻塞挂起等待进程1释放锁。
//此时进程1使用的是独占锁,进程2使用的是共享锁,所以如上语言总结。
//即如果一个任务获得一个文件的独占锁,那么其它任务就不能再获得同一文件的独占锁或共享锁,直到独占锁被释放。
//此处便是这种情况。
//进程1代码片段
RandomAccessFile randomAccessFile = new RandomAccessFile("test.txt", "rw");
FileChannel channel = randomAccessFile.getChannel();
FileLock lock = channel.lock(0, Integer.MAX_VALUE, true);
for (int i = 0; i < 10; i++) {
    randomAccessFile.writeChars("1");
    Thread.sleep(1000);
}
lock.release();
randomAccessFile.close();
channel.close();

////进程2代码片段
RandomAccessFile randomAccessFile = new RandomAccessFile("test.txt", "rw");
FileChannel channel = randomAccessFile.getChannel();
FileLock lock = channel.lock(0, Integer.MAX_VALUE, true);
randomAccessFile.length();
lock.release();
randomAccessFile.close();
channel.close();

//如上程序先运行进程1,然后同时启动进程2。
//会发现进程1会抛出IOException: 另一个程序已锁定文件的一部分,进程无法访问。at java.io.RandomAccessFile.writeBytes(Native Method) 异常。
//而进程2可以正常执行。
//此时进程1、2都使用的是共享锁,如上语言总结。
//共享锁是不允许写操作的,只允许读操作,所以进程1抛出写异常(Windows上)。
//进程1代码片段
RandomAccessFile randomAccessFile = new RandomAccessFile("test.txt", "rw");
FileChannel channel = randomAccessFile.getChannel();
FileLock lock = channel.lock(0, Integer.MAX_VALUE, true);
for (int i = 0; i < 10; i++) {
    randomAccessFile.readLine();
    Thread.sleep(1000);
}
lock.release();
randomAccessFile.close();
channel.close();

////进程2代码片段
RandomAccessFile randomAccessFile = new RandomAccessFile("test.txt", "rw");
FileChannel channel = randomAccessFile.getChannel();
FileLock lock = channel.lock(0, Integer.MAX_VALUE, true);
randomAccessFile.length();
lock.release();
randomAccessFile.close();
channel.close();

//如上程序先运行进程1,然后同时启动进程2。
//会发现进程1、2都能无阻塞的执行。
//此时进程1、2都使用的是共享锁,如上语言总结。
//共享锁是不允许写操作的,只允许读操作,所以他们都能正常并发读操作。

所以综上案例可以再次总结如下:

  • 当使用独占锁时,除了该进程其他的进程都不能访问被锁定的文件,其他进程会阻塞在 lock() 代码处。

  • 当使用独占锁时,该进程可以对文件进行写操作,因为此时其他试图获取锁的进程会被阻塞,此时执行写操作是并发安全的。

  • 当使用共享锁时,所有其他进程都可以获取锁,并且可以并行的进行读操作。

  • 当使用共享锁时,调用 write 方法修改文件会抛出异常,因为所有的进程都可以获取读文件锁,执行写操作是并发不安全的。

所以以后在使用 FileLock 时请分清场景择优选择合适的锁机制策略,不要再混为一谈了。

参考原文:
多进程文件锁 FileLock 相关实战场景题目完全解析

你可能感兴趣的:(高并发,java)