在单个JVM内和跨多个JVM使用java文件锁
我想我错过了一些东西,但我无法理解文件锁在Java中是如何工作的。更确切地说,它是如何实现的 似乎我无法在单个JVM中为同一个文件获取(甚至无法尝试获取)两个或多个锁。将成功获取第一个锁,所有进一步获取更多锁的尝试将导致OverlappingFileLockException。然而,它适用于单独的过程 我想实现由文件系统支持的数据存储,该系统用于处理多个并发请求(读和写)。我想使用文件锁来锁定存储中的特定文件 似乎我必须在JVM级别上引入更多的同步(独占),然后才在文件上进行同步,以避免此异常 有人做过那样的事吗 我准备了一个简单的测试用例来说明我的问题是什么。我使用MacOSX,Java6在单个JVM内和跨多个JVM使用java文件锁,java,nio,file-locking,Java,Nio,File Locking,我想我错过了一些东西,但我无法理解文件锁在Java中是如何工作的。更确切地说,它是如何实现的 似乎我无法在单个JVM中为同一个文件获取(甚至无法尝试获取)两个或多个锁。将成功获取第一个锁,所有进一步获取更多锁的尝试将导致OverlappingFileLockException。然而,它适用于单独的过程 我想实现由文件系统支持的数据存储,该系统用于处理多个并发请求(读和写)。我想使用文件锁来锁定存储中的特定文件 似乎我必须在JVM级别上引入更多的同步(独占),然后才在文件上进行同步,以避免此异常
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();
}
}
每个文件只能获取一次锁。锁不可重入AFAIK
IMHO:使用文件在进程之间进行通信是一个非常糟糕的主意。也许你能让它可靠地工作,如果可以,请告诉我;) 我将在一个进程中读取/写入一个且只有一个线程。您检查了吗?
FileChannel.lock()
方法返回整个文件的独占锁。如果希望多个锁在不同线程之间同时处于活动状态,则不能使用此方法
相反,您需要使用FileChannel.locklock(长位置、长大小、布尔共享)
来锁定文件的特定区域。这将允许您同时激活多个锁,前提是每个锁都应用于文件的不同区域。如果两次尝试锁定文件的同一区域,将遇到相同的异常。来自Javadoc:
文件锁是代表
整个Java虚拟机。他们是
不适合控制访问
文件中的多个线程创建的文件
相同的虚拟机
这并不完全正确。可以获取文件特定区域的锁。因此,只要每个锁指定不同的、不重叠的区域,就可以为单个文件获取多个锁。共享存储似乎是进程(可能在不同主机上运行)之间通信的可能方式之一。无论您使用什么存储—S3、数据库或共享文件系统。使用数据库,单个进程管理文件,您可以间接访问其文件。我发现在多台主机上以高性能和可靠的方式更新原始文件是非常困难的。@Yakov,如果你不想使用SQL数据库,你可以尝试“使用文件在进程之间通信是一个非常糟糕的主意”。@Peter Lawrey我想知道为什么?最简单的用例比数据库、JMS和其他任何东西都要轻得多。那为什么这是个坏主意?你能指出什么文献吗?我希望整个文件都有锁(读或写,视情况而定),因为我的存储器中有多个文件(每个文件都很小)。顺便说一句,aroth,你看到我的片段了吗。看来我永远也不会理解JDK中实现锁的家伙的逻辑。为什么(!)可以在多个进程中获取读锁,或者如果我尝试在另一个进程中获取它,排他锁将阻塞线程,但是当我在同一个JVM中获取锁时,我会得到异常。调用lock()时,我希望当前线程阻塞,直到获得锁为止。如果我不想阻塞,我可以使用tryLock。@Yakov-我明白你的意思,抛出异常而不是让调用方阻塞等待锁是令人困惑的。但是也许您可以使用类似于
的东西来解决这个问题((lock=chan.tryLock())==null){Thread.sleep(100);}
?或者您可以使用类似于synchronized(chan){lock=chan.lock();/…}
的模式,以防止在同一个JVM中调用lock()
两次?tryLock并不总是有效-有时我在尝试释放锁时会在FileLockImpl中的某个地方得到NPE(源代码不包括在JDK中)。因此,我认为,这里唯一的解决方案是添加JVM级别的同步。谢谢。还有一个问题。当需要文件锁的对象类由不同的类加载器加载时(例如tomcat中的不同应用程序或类似程序),JVM同步可能非常棘手。这是正确的,但并不能解决真正的问题,即锁由进程而不是线程持有。