4

我有一种write方法可以安全地将数据写入文件。

// The current file I am writing to.
FileOutputStream file = null;
...
// Synchronized version.
private void write(byte[] bytes) {
  if (file != null && file.getChannel() != null) {
    try {
      boolean written = false;
      do {
        try {
          // Lock it!
          FileLock lock = file.getChannel().lock();
          try {
            // Write the bytes.
            file.write(bytes);
            written = true;
          } finally {
            // Release the lock.
            lock.release();
          }

        } catch (OverlappingFileLockException ofle) {
          try {
            // Wait a bit
            Thread.sleep(0);
          } catch (InterruptedException ex) {
            throw new InterruptedIOException("Interrupted waiting for a file lock.");
          }
        }
      } while (!written);
    } catch (IOException ex) {
      log.warn("Failed to lock " + fileName, ex);
    }
  } else {
    log.warn("Failing - " + (file == null ? "file" : "channel") + " is null!!");
  }
}

它对我来说已经有一段时间了,虽然我知道它有一些皱纹。

我最近更改了一个项目,该项目使用此代码在 Java 5(来自 Java 6)下构建和运行,现在它看起来像死锁等待文件锁定。它是一个多线程应用程序,多个线程很有可能尝试写入同一个文件。

调试器告诉我挂起的线程正在等待FileLock lock = file.getChannel().lock()调用返回。

一些研究提出了这个有趣的小金块,其中提到:

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

那我做错了吗?如果是这样,正确的方法是什么?如果我做得对,我怎么会陷入僵局?

补充:忘了提 - 每个线程都有自己的这个对象的副本,所以代码中不应该有任何同步问题。我觉得依靠这种FileChannel.lock()方法来确保写入不会交错是安全的。

也补充说:我确实使用各种synchronized机制解决了这个问题。但是,我确实有一些悬而未决的问题:

  1. 为什么FileLock lock = file.getChannel().lock(); 不适合...
  2. 为什么我的问题只在切换回 Java-5 时才出现,而 Java-6 一切正常?
4

2 回答 2

4

FileLock仅用于进程间锁定,javadoc读取:

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

要在 java 线程(相同的 JVM)之间锁定,您需要使用一些共享锁。我建议在文件写入类中使用同步块(根据这些 文章,它可能表现最好):

final Object lock = new Object();

public void write(...){
  synchronized(lock){
    // do writing
  }
}

另一种方法是使用ReentrantLock然后使用经过验证的习语

final ReentrantLock lock = new ReentrantLock();

public void write(...){
  try {
    lock.lock()
    // do the writing
  } finally {
    // forget this and you're screwed
    lock.unlock();
  }
}
于 2012-07-18T15:20:48.347 回答
1

您可能需要使用文件而不是哈希图在实际代码上实现临界区概念。您可以创建一个同步块或将文件访问代码分离到一个单独的过程中并使该方法同步。

本质上,一次只有一个线程执行同步块。它为您提供所需的独家访问权限。

另一种方法是使用串行线程执行器,具体取决于您的功能要求。

你可能想看看这个线程: Howto synchronize file access in a shared folder using Java (OR: ReadWriteLock on network level)

于 2012-07-18T14:44:11.070 回答