Java file locking on a network

2019-02-21 11:37发布

This is perhaps similar to previous posts, but I want to be specific about the use of locking on a network, rather than locally. I want to write a file to a shared location, so it may well go on a network (certainly a Windows network, maybe Mac). I want to prevent other people from reading any part of this file whilst it it being written. This will not be a highly concurrent process, and the files will be typically less than 10MB.

I've read the FileLock documentation and File documentation and am left somewhat confused, as to what is safe and what is not. I want to lock the entire file, rather than portions of it.

Can I use FileChannel.tryLock(), and it is safe on a network, or does it depend on the type of network? Will it work on a standard Windows network (if there is such a thing).

If this does not work, is the best thing to create a zero byte file or directory as a lock file, and then write out the main file. Why does that File.createNewFile() documentation say don't use this for file locking? I appreciate this is subject to race conditions, and is not ideal.

4条回答
劫难
2楼-- · 2019-02-21 12:14

This can't be reliably done on a network file system. As long as your application is the only application that accesses the file, it's best to implement some kind of cooperative locking process (perhaps writing a lock file to the network filesystem when you open the file). The reason that is not recommended, however, is that if your process crashes or the network goes down or any other number of issues happen, your application gets into a nasty, dirty state.

查看更多
淡お忘
3楼-- · 2019-02-21 12:21

You can have a empty file which is lying on the server you want to write to.

When you want to write to the server you can catch the token. Only when you have the token you should write to any file which is lying on the server.

When you are ready with you file operations or an exception was thrown you have to release the token.

The helper class can look like

private FileLock lock;

private File tokenFile;

public SLTokenLock(String serverDirectory) {
    String tokenFilePath = serverDirectory + File.separator + TOKEN_FILE;
    tokenFile = new File(tokenFilePath);
}

public void catchCommitToken() throws TokenException {
    RandomAccessFile raf;
    try {
        raf = new RandomAccessFile(tokenFile, "rw"); //$NON-NLS-1$
        FileChannel channel = raf.getChannel();
        lock = channel.tryLock();

        if (lock == null) {
            throw new TokenException(CANT_CATCH_TOKEN);
        }
    } catch (Exception e) {
        throw new TokenException(CANT_CATCH_TOKEN, e);
    }
}

public void releaseCommitToken() throws TokenException {
    try {
        if (lock != null && lock.isValid()) {
            lock.release();
        }
    } catch (Exception e) {
        throw new TokenException(CANT_RELEASE_TOKEN, e);
    }
}

Your operations then should look like

try {
        token.catchCommitToken();

        // WRITE or READ to files inside the directory
    } finally {
        token.releaseCommitToken();
    }
查看更多
干净又极端
4楼-- · 2019-02-21 12:30

I found this bug report which describes why the note about file locking was added to the File.createNewFile documentation.

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4676183

It states:

If you mark the file as deleteOnExit before invoking createNewFile but the file already exists, you run the risk of deleting a file you didn't create and dropping someone elses lock! On the other hand, if you mark the file after creating it, you lose atomicity: if the program exits before the file is marked, it won't get deleted and the lock will be "wedged".

So it looks like the main reason locking is discouraged with File.createNewFile() is that you can end up with orphaned lock files if the JVM unexpectedly terminates before you have a chance to delete it. If you can deal with orphaned lock files then it could be used as a simple locking mechanism. However, I wouldn't recommend the method suggested in the comments of the bug report as it has race conditions around read/writing the timestamp value and reclaiming the expired lock.

查看更多
唯我独甜
5楼-- · 2019-02-21 12:36

Rather than implementing a locking strategy which will, in all likelihood, rely on readers to adhere to your convention but will not force them to, perhaps you can write the file out to a hidden or obscurely named file where it will be effectively invisible to readers. When the write operation is complete, rename the file to the expected public name.

The downside is that hiding and/or renaming without additional IO may require you to use native OS commands, but the procedure to do so should be fairly simple and deterministic.

查看更多
登录 后发表回答