欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

理解Java文件锁FileLock在多线程环境下的工作机制详解

最编程 2024-07-28 18:16:57
...
问:下面程序能正常运行吗?分析一下你的看法?
public class Test {
    private static void thread1() {
        try {
            RandomAccessFile randomAccessFile = new RandomAccessFile("test.txt", "rw");
            FileChannel channel = randomAccessFile.getChannel();

            FileLock lock = channel.lock();
            System.out.println("write get lock.");

            for (int i = 0; i < 5; i++) {
                String content = "{"+i+"}"+new Date()+"\n";
                randomAccessFile.writeChars(content);
                System.out.println("write content:" + content);
                Thread.sleep(1000);
            }
            lock.release();
            System.out.println("write release lock.");

            randomAccessFile.close();
            channel.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void thread2() {
        try {
            RandomAccessFile randomAccessFile = new RandomAccessFile("test.txt", "rw");
            FileChannel channel = randomAccessFile.getChannel();
            Thread.sleep(2000);

            FileLock lock = null;
            do {
                lock = channel.tryLock();
            } while (null == lock);
            System.out.println("read get lock.");

            System.out.println("read get content lines:" + randomAccessFile.length());

            lock.release();
            System.out.println("read release lock.");

            randomAccessFile.close();
            channel.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new Thread("T1") {
            @Override
            public void run() {
                thread1();
            }
        }.start();

        new Thread("T2") {
            @Override
            public void run() {
                thread2();
            }
        }.start();
    }
}

答:可以运行,只是得不到程序想要的效果,因为上面程序的运行结果会是类似如下输出:

write get lock.
write content:{0}Fri Aug 10 10:14:49 CST 2018
write content:{1}Fri Aug 10 10:14:50 CST 2018
//thread2()方法的 tryLock 抛出异常
java.nio.channels.OverlappingFileLockException
    at sun.nio.ch.SharedFileLockTable.checkList(FileLockTable.java:255)
    at sun.nio.ch.SharedFileLockTable.add(FileLockTable.java:152)
    at sun.nio.ch.FileChannelImpl.tryLock(FileChannelImpl.java:1108)
    at java.nio.channels.FileChannel.tryLock(FileChannel.java:1155)
    at Test.thread2(Test.java:39)
    at Test.access$100(Test.java:6)
    at Test$2.run(Test.java:66)

write content:{2}Fri Aug 10 10:14:51 CST 2018
write content:{3}Fri Aug 10 10:14:52 CST 2018
write content:{4}Fri Aug 10 10:14:53 CST 2018
write release lock.

首先上面例子中 lock() 和 tryLock() 的区别如下:

  • lock() 为阻塞方法,锁定范围可以随着文件的增大而增加,无参 lock() 默认为独占锁,有参 lock(0L, Long.MAX_VALUE, true) 为共享锁。

  • tryLock() 为非阻塞方法,当未获得锁时返回 null,无参 tryLock() 默认为独占锁,有参 tryLock(0L, Long.MAX_VALUE, true) 为共享锁。

此外 FileLock 的生命周期在调用 FileLock.release() 或者 Channel.close() 或者 JVM 关闭时才会结束。

而我们上面程序之所以会抛出异常是因为在同一进程内,文件锁 FileLock 没有被释放之前不可以再次获取(即在 release() 方法调用前只能 lock() 或 tryLock() 一次),因为文件锁 FileLock 的效果是与操作系统相关的,所以上面程序会抛出文件重叠锁异常。

要想让上面程序在多线程及多进程中同时可用,则最简单的修改思路可以参考把 lock-release 部分再用 Java 线程锁(譬如 synchronized)进行包起来即可。当然这只是建议,具体还是要取决于业务多线程及进程同步并发场景的需求进行设计改良。

本文參考自 文件锁 FileLock 在多线程中的挖坑题解析

推荐阅读