5

我想我错过了一些东西,但我无法理解文件锁在 Java 中是如何工作的。更准确地说 - 它是如何实施的。

似乎我无法为单个 JVM 中的同一文件获取(甚至无法尝试获取)两个或更多锁。第一个锁将成功获取,所有进一步尝试获取更多锁将导致 OverlapingFileLockException。然而,它适用于单独的进程。

我想实现由文件系统支持的数据存储,该文件系统旨在处理多个并发请求(读取和写入)。我想使用文件锁来锁定存储中的特定文件。

看来我必须在 JVM 级别上再引入一个同步(独占),然后才在文件上同步以避免此异常。

有没有人做过这样的事?

我准备了简单的测试用例来说明我的问题是什么。我使用 Mac OS X、Java 6。

import junit.framework.*;

import javax.swing.*;
import java.io.*;
import java.nio.channels.*;

/**
 * Java file locks test.
 */
public class FileLocksTest extends TestCase {
    /** File path (on Windows file will be created under the root directory of the current drive). */
    private static final String LOCK_FILE_PATH = "/test-java-file-lock-tmp.bin";

    /**
     * @throws Exception If failed.
     */
    public void testWriteLocks() throws Exception {
        final File file = new File(LOCK_FILE_PATH);

        file.createNewFile();

        RandomAccessFile raf = new RandomAccessFile(file, "rw");

        System.out.println("Getting lock...");

        FileLock lock = raf.getChannel().lock();

        System.out.println("Obtained lock: " + lock);

        Thread thread = new Thread(new Runnable() {
            @Override public void run() {
                try {
                    RandomAccessFile raf = new RandomAccessFile(file, "rw");

                    System.out.println("Getting lock (parallel thread)...");

                    FileLock lock = raf.getChannel().lock();

                    System.out.println("Obtained lock (parallel tread): " + lock);

                    lock.release();
                }
                catch (Throwable e) {
                    e.printStackTrace();
                }
            }
        });

        thread.start();

        JOptionPane.showMessageDialog(null, "Press OK to release lock.");

        lock.release();

        thread.join();
    }

    /**
     * @throws Exception If failed.
     */
    public void testReadLocks() throws Exception {
        final File file = new File(LOCK_FILE_PATH);

        file.createNewFile();

        RandomAccessFile raf = new RandomAccessFile(file, "r");

        System.out.println("Getting lock...");

        FileLock lock = raf.getChannel().lock(0, Long.MAX_VALUE, true);

        System.out.println("Obtained lock: " + lock);

        Thread thread = new Thread(new Runnable() {
            @Override public void run() {
                try {
                    RandomAccessFile raf = new RandomAccessFile(file, "r");

                    System.out.println("Getting lock (parallel thread)...");

                    FileLock lock = raf.getChannel().lock(0, Long.MAX_VALUE, true);

                    System.out.println("Obtained lock (parallel thread): " + lock);

                    lock.release();

                }
                catch (Throwable e) {
                    e.printStackTrace();
                }
            }
        });

        thread.start();

        JOptionPane.showMessageDialog(null, "Press OK to release lock.");

        lock.release();

        thread.join();
    }
}
4

3 回答 3

8

来自 Javadoc:

文件锁代表整个 Java 虚拟机持有。它们不适用于控制同一虚拟机内的多个线程对文件的访问。

于 2011-04-20T12:21:57.627 回答
2

你检查过文档吗?该FileChannel.lock()方法返回一个跨整个文件的排他锁。如果你想在不同的线程上同时激活多个锁,那么你不能使用这个方法。

相反,您需要使用FileChannel.locklock(long position, long size, boolean shared)它来锁定文件的特定区域。这将允许您同时激活多个锁,前提是每个锁都应用于文件的不同区域。如果您尝试两次锁定文件的同一区域,您将遇到相同的异常。

于 2011-04-19T11:17:30.517 回答
2

每个文件只能获取一次锁。锁不是可重入的AFAIK。

恕我直言:使用文件在进程之间进行通信是一个非常糟糕的主意。也许你可以让它可靠地工作,如果可以的话,请告诉我;)

我将只有一个线程在一个进程中读/写。

于 2011-04-19T11:08:10.343 回答