如何在Java servlet中同步文件访问?

5
我创建了一个小型Java servlet,用于简单的目的:一旦被调用,它将执行以下步骤:
  1. 从本地文件系统中读取文件foo.json
  2. 处理文件中的数据并进行一些更改
  3. 将更改写回文件
下面是代码的简化版本:
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    FileInputStream inputStream = new FileInputStream("foo.json");
    String filecontent = IOUtils.toString(inputStream);
    inputStream.close();

    JSONObject json = new JSONObject(filecontent);

    doSomeChangesTo(json);

    FileWriter writer = new FileWriter("foo.json");
    writer.write(json.toJSONString());
    writer.flush();
    writer.close();
}

现在我面临的问题是,当两个或多个http请求同时调用servlet时,可能会发生问题。为了避免对同一文件进行多个并行写访问,我需要以某种方式同步此过程。从我对servlet生命周期过程的理解来看,每个请求都会生成一个新线程,因此使用FileLock可能没有任何影响:
“文件锁代表整个Java虚拟机进行持有。它们不适用于控制在同一虚拟机中由多个线程对文件的访问。”
(来自http://docs.oracle.com/javase/7/docs/api/java/nio/channels/FileLock.html
我猜想使用synchronized()关键字也不起作用,因为我想要同步文件系统访问而不是变量/对象的访问。
那么,在我的servlet上发生多个并行请求时,如何同步文件系统访问?

在处理文件时更改文件名?我猜使用数据库而不是文件并不是由您决定的决策? - Fildor
如果你的应用程序是唯一写入文件的(而且不是集群化的),同步会很好用。人们通常使用数据库来处理并发访问。 - JB Nizet
你到底想要在这里同步什么?是整个读取-处理-写入的过程吗?还是只有单独的读取和写入,而处理可以交织进行(并且你可能会从旧的读取中写回数据)? - Ordous
@Fildor 正确,目前不能使用数据库作为选项。 - Timo Ernst
@JBNizet 我只需将我发布的代码放入synchronized(){}中,就可以了吗? - Timo Ernst
显示剩余2条评论
3个回答

8
我想使用synchronized(){}关键字也不会起作用,因为我想同步文件系统访问而不是变量/对象的访问。
使用synchronized可以解决问题。您认为如果要从多个线程控制对对象X的访问,则必须在该对象上使用synchronized。其实不需要如此。只要所有访问都使用相同的对象,就可以在任何对象上使用synchronized
事实上,更好的做法通常是使用单独的private锁对象进行同步,因为这样外部代码就不可能在锁上进行同步。
因此,您可能会像这样针对每个共享文件有一个实例:
 public class SharedFile
 {
      private final File path;
      private final Object lock = new Object();

      public SharedFile(File path) {
         this.path = path;
      }

      public void process(.....) throws IOException {
         synchronized(lock) {
            try(InputStream = new FileInputStream(path)) {
               ....
            }
         }
      }
 }

不,锁对象在这里很重要。 - mavroprovato

3
您可以使用一个Semaphore,如下所示:
private static Semaphore semaphore = new Semaphore(1);

public void doSomeChangesTo(JSONObject json) {
    try {
        semaphore.acquire();

        // doSomeChangesTo

    } finally {
        semaphore.release();
    }
}

即使该对象被多个子类使用,我可以使用这个概念吗?@troig - hanish
嗨@hanish!不确定我是否明白你的意思,但我没有看到任何问题。 - troig

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接