随机访问文件 FileLock:java.io 与 java.nio
Random access file FileLock: java.io vs. java.nio
我注意到 java.io
and java.nio
implementations of random access files differ slightly with respect to how FileLocks
已处理。
看起来好像(在 Windows 上)java.io
给了你一个强制文件锁,java.nio
给了你一个咨询文件锁,分别在请求时。强制文件锁意味着锁适用于所有进程,建议适用于遵循相同锁定协议的行为良好的进程。
如果我运行下面的例子,我可以手动删除*.nio
文件,而*.io
文件拒绝删除。
import java.io.*;
import java.lang.management.ManagementFactory;
import java.nio.*;
import java.nio.channels.*;
import java.nio.file.*;
public class NioIoLock {
public static void main(String[] args) throws IOException, InterruptedException {
String workDir = System.getProperty("user.dir");
FileChannel channelIo, channelNio;
FileLock lockIo, lockNio;
// use io
{
String fileName = workDir
+ File.separator
+ ManagementFactory.getRuntimeMXBean().getName()
+ ".io";
File lockFile = new File(fileName);
lockFile.deleteOnExit();
RandomAccessFile file = new RandomAccessFile(lockFile, "rw");
channelIo = file.getChannel();
lockIo = channelIo.tryLock();
if (lockIo != null) {
channelIo.write(ByteBuffer.wrap("foobar".getBytes("UTF-8")));
}
}
// use nio
{
Path workDirPath = Paths.get(workDir);
Path file = workDirPath.resolve(
Paths.get(ManagementFactory.getRuntimeMXBean().getName() + ".nio"));
// open/create test file
channelNio = FileChannel.open(
file, StandardOpenOption.READ, StandardOpenOption.WRITE,
StandardOpenOption.CREATE, StandardOpenOption.DELETE_ON_CLOSE);
// lock file
lockNio = channelNio.tryLock();
if (lockNio != null) {
channelNio.write(ByteBuffer.wrap("foobar".getBytes("UTF-8")));
}
}
// do not release locks for some time
Thread.sleep(10000);
// release io lock and channel
if (lockIo != null && lockIo.isValid()) {
lockIo.release();
}
channelIo.close();
// release nio lock and channel
if (lockNio != null && lockNio.isValid()) {
lockNio.release();
}
channelNio.close();
}
}
这有什么原因吗?这两者是否被视为替代品,或者它们是否意味着无限期共存?
TL;DR: 这与锁无关,与打开文件的方式有关。您在 io
中看到的是 Windows 在 Windows 2000 年之前所能做的最好的事情,即使文件以只读方式打开也是如此——无法删除它文件。您在 nio
中看到的是使用自 Windows 2000 年以来引入的新功能的改进,但如果您愿意,您仍然可以在 nio
中使用旧行为。决定不将该功能移植到 io
所做的事情中。
全文: 我删除了所有与锁定(见下文)以及写入文件相关的代码,它的工作原理与您的代码完全相同;但是,我还发现如果您指定 ExtendedOpenOption.NOSHARE_DELETE
when opening the "nio" channel, then behavior when you try to delete any of the two files is the same (uncomment it in the code and try). Also found 并且它有一些解释,不确定是否有帮助。
import java.io.*;
import java.lang.management.ManagementFactory;
import java.nio.*;
import java.nio.channels.*;
import java.nio.file.*;
public class NioIoLock {
public static void main(String[] args)
throws IOException, InterruptedException {
String workDir = System.getProperty("user.dir");
FileChannel channelIo, channelNio;
FileLock lockIo, lockNio;
// use io
{
String fileName = workDir + File.separator
+ ManagementFactory.getRuntimeMXBean().getName() + ".io";
File lockFile = new File(fileName);
lockFile.deleteOnExit();
RandomAccessFile file = new RandomAccessFile(lockFile, "rw");
channelIo = file.getChannel();
}
// use nio
{
Path workDirPath = Paths.get(workDir);
Path file = workDirPath.resolve(Paths
.get(ManagementFactory.getRuntimeMXBean().getName() + ".nio"));
// open/create test file
channelNio = FileChannel.open(file, StandardOpenOption.READ,
StandardOpenOption.WRITE, StandardOpenOption.CREATE,
StandardOpenOption.DELETE_ON_CLOSE
/*, com.sun.nio.file.ExtendedOpenOption.NOSHARE_DELETE*/);
}
// do not release locks for some time
Thread.sleep(10000);
}
}
更新 1: 实际上,我仍然不知道这背后的基本原理,但机制很清楚:RandomAccessFile
构造函数最终调用以下本机代码method winFileHandleOpen 来自 io_util_md.c:
FD
winFileHandleOpen(JNIEnv *env, jstring path, int flags)
{
...
const DWORD sharing =
FILE_SHARE_READ | FILE_SHARE_WRITE;
... // "sharing" not updated anymore
h = CreateFileW(
pathbuf, /* Wide char path name */
access, /* Read and/or write permission */
sharing, /* File sharing flags */
NULL, /* Security attributes */
disposition, /* creation disposition */
flagsAndAttributes, /* flags and attributes */
NULL);
...
}
因此,FILE_SHARE_DELETE
标志未设置,您无法设置它。另一方面,调用 java.nio.channels.FileChannel.open(Path, OpenOption...)
eventually 会考虑这个标志:
private static FileDescriptor open(String pathForWindows,
String pathToCheck,
Flags flags,
long pSecurityDescriptor)
throws WindowsException
{
...
int dwShareMode = 0;
if (flags.shareRead)
dwShareMode |= FILE_SHARE_READ;
if (flags.shareWrite)
dwShareMode |= FILE_SHARE_WRITE;
if (flags.shareDelete)
dwShareMode |= FILE_SHARE_DELETE;
...
// open file
long handle = CreateFile(pathForWindows,
dwDesiredAccess,
dwShareMode,
pSecurityDescriptor,
dwCreationDisposition,
dwFlagsAndAttributes);
...
}
更新 2: 来自 JDK-6607535:
The suggestion to change the sharing mode is RFE 6357433. It would be great to do that but it may break existing applications (since the current behavior has existed since jdk1.0). Adding a special mode to RandomAccessFile to work around a Windows issues is probably not the right approach either. Also, there is some suggestion that this can help with the issue of deleting files that have a file mapping. This is not so, as a file mapping still prevents a file from being deleted. Anyway, for jdk7 we are working on a new file system API that will address some of the requirements here. The Windows implementation does the right thing so that opened files can be deleted.
另请参阅从那里引用的 JDK-6357433:
A customer has pointed out on the java.net forums that opening a file on Windows using a FileInputStream causes other processes to be unable to delete the file. This is intended behavior as the code is currently written, as the sharing flags specified during the opening of the file are FILE_SHARE_READ and FILE_SHARE_WRITE. See io_util_md.c, fileOpen. However, Windows 2000 supplies a new flag, FILE_SHARE_DELETE, which allows another process to delete the file while it is still open.
最后
WORK AROUND
For jdk7, the workaround is to use new file system API. So instead of new FileInputStream(f) and new FileOutputStream(f), use f.toPath().newInputStream() and f.toPath().newOutputStream().
我注意到 java.io
and java.nio
implementations of random access files differ slightly with respect to how FileLocks
已处理。
看起来好像(在 Windows 上)java.io
给了你一个强制文件锁,java.nio
给了你一个咨询文件锁,分别在请求时。强制文件锁意味着锁适用于所有进程,建议适用于遵循相同锁定协议的行为良好的进程。
如果我运行下面的例子,我可以手动删除*.nio
文件,而*.io
文件拒绝删除。
import java.io.*;
import java.lang.management.ManagementFactory;
import java.nio.*;
import java.nio.channels.*;
import java.nio.file.*;
public class NioIoLock {
public static void main(String[] args) throws IOException, InterruptedException {
String workDir = System.getProperty("user.dir");
FileChannel channelIo, channelNio;
FileLock lockIo, lockNio;
// use io
{
String fileName = workDir
+ File.separator
+ ManagementFactory.getRuntimeMXBean().getName()
+ ".io";
File lockFile = new File(fileName);
lockFile.deleteOnExit();
RandomAccessFile file = new RandomAccessFile(lockFile, "rw");
channelIo = file.getChannel();
lockIo = channelIo.tryLock();
if (lockIo != null) {
channelIo.write(ByteBuffer.wrap("foobar".getBytes("UTF-8")));
}
}
// use nio
{
Path workDirPath = Paths.get(workDir);
Path file = workDirPath.resolve(
Paths.get(ManagementFactory.getRuntimeMXBean().getName() + ".nio"));
// open/create test file
channelNio = FileChannel.open(
file, StandardOpenOption.READ, StandardOpenOption.WRITE,
StandardOpenOption.CREATE, StandardOpenOption.DELETE_ON_CLOSE);
// lock file
lockNio = channelNio.tryLock();
if (lockNio != null) {
channelNio.write(ByteBuffer.wrap("foobar".getBytes("UTF-8")));
}
}
// do not release locks for some time
Thread.sleep(10000);
// release io lock and channel
if (lockIo != null && lockIo.isValid()) {
lockIo.release();
}
channelIo.close();
// release nio lock and channel
if (lockNio != null && lockNio.isValid()) {
lockNio.release();
}
channelNio.close();
}
}
这有什么原因吗?这两者是否被视为替代品,或者它们是否意味着无限期共存?
TL;DR: 这与锁无关,与打开文件的方式有关。您在 io
中看到的是 Windows 在 Windows 2000 年之前所能做的最好的事情,即使文件以只读方式打开也是如此——无法删除它文件。您在 nio
中看到的是使用自 Windows 2000 年以来引入的新功能的改进,但如果您愿意,您仍然可以在 nio
中使用旧行为。决定不将该功能移植到 io
所做的事情中。
全文: 我删除了所有与锁定(见下文)以及写入文件相关的代码,它的工作原理与您的代码完全相同;但是,我还发现如果您指定 ExtendedOpenOption.NOSHARE_DELETE
when opening the "nio" channel, then behavior when you try to delete any of the two files is the same (uncomment it in the code and try). Also found
import java.io.*;
import java.lang.management.ManagementFactory;
import java.nio.*;
import java.nio.channels.*;
import java.nio.file.*;
public class NioIoLock {
public static void main(String[] args)
throws IOException, InterruptedException {
String workDir = System.getProperty("user.dir");
FileChannel channelIo, channelNio;
FileLock lockIo, lockNio;
// use io
{
String fileName = workDir + File.separator
+ ManagementFactory.getRuntimeMXBean().getName() + ".io";
File lockFile = new File(fileName);
lockFile.deleteOnExit();
RandomAccessFile file = new RandomAccessFile(lockFile, "rw");
channelIo = file.getChannel();
}
// use nio
{
Path workDirPath = Paths.get(workDir);
Path file = workDirPath.resolve(Paths
.get(ManagementFactory.getRuntimeMXBean().getName() + ".nio"));
// open/create test file
channelNio = FileChannel.open(file, StandardOpenOption.READ,
StandardOpenOption.WRITE, StandardOpenOption.CREATE,
StandardOpenOption.DELETE_ON_CLOSE
/*, com.sun.nio.file.ExtendedOpenOption.NOSHARE_DELETE*/);
}
// do not release locks for some time
Thread.sleep(10000);
}
}
更新 1: 实际上,我仍然不知道这背后的基本原理,但机制很清楚:RandomAccessFile
构造函数最终调用以下本机代码method winFileHandleOpen 来自 io_util_md.c:
FD
winFileHandleOpen(JNIEnv *env, jstring path, int flags)
{
...
const DWORD sharing =
FILE_SHARE_READ | FILE_SHARE_WRITE;
... // "sharing" not updated anymore
h = CreateFileW(
pathbuf, /* Wide char path name */
access, /* Read and/or write permission */
sharing, /* File sharing flags */
NULL, /* Security attributes */
disposition, /* creation disposition */
flagsAndAttributes, /* flags and attributes */
NULL);
...
}
因此,FILE_SHARE_DELETE
标志未设置,您无法设置它。另一方面,调用 java.nio.channels.FileChannel.open(Path, OpenOption...)
eventually 会考虑这个标志:
private static FileDescriptor open(String pathForWindows,
String pathToCheck,
Flags flags,
long pSecurityDescriptor)
throws WindowsException
{
...
int dwShareMode = 0;
if (flags.shareRead)
dwShareMode |= FILE_SHARE_READ;
if (flags.shareWrite)
dwShareMode |= FILE_SHARE_WRITE;
if (flags.shareDelete)
dwShareMode |= FILE_SHARE_DELETE;
...
// open file
long handle = CreateFile(pathForWindows,
dwDesiredAccess,
dwShareMode,
pSecurityDescriptor,
dwCreationDisposition,
dwFlagsAndAttributes);
...
}
更新 2: 来自 JDK-6607535:
The suggestion to change the sharing mode is RFE 6357433. It would be great to do that but it may break existing applications (since the current behavior has existed since jdk1.0). Adding a special mode to RandomAccessFile to work around a Windows issues is probably not the right approach either. Also, there is some suggestion that this can help with the issue of deleting files that have a file mapping. This is not so, as a file mapping still prevents a file from being deleted. Anyway, for jdk7 we are working on a new file system API that will address some of the requirements here. The Windows implementation does the right thing so that opened files can be deleted.
另请参阅从那里引用的 JDK-6357433:
A customer has pointed out on the java.net forums that opening a file on Windows using a FileInputStream causes other processes to be unable to delete the file. This is intended behavior as the code is currently written, as the sharing flags specified during the opening of the file are FILE_SHARE_READ and FILE_SHARE_WRITE. See io_util_md.c, fileOpen. However, Windows 2000 supplies a new flag, FILE_SHARE_DELETE, which allows another process to delete the file while it is still open.
最后
WORK AROUND
For jdk7, the workaround is to use new file system API. So instead of new FileInputStream(f) and new FileOutputStream(f), use f.toPath().newInputStream() and f.toPath().newOutputStream().