Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to synchronize file access in a Java servlet?

I created a small Java servlet for a simple purpose: Once it is called, it will do the following steps:

  1. Read file foo.json from the local filesystem
  2. Process the data from the file and do some changes to it
  3. Write back the changes to the file

Simplified version of the code:

@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();
}

Now I am facing the problem that it could happen that the servlet is called nearly at the same time by two or more http requests to the servlet. To avoid multiple parallel write access on the same file I need to synchronize this somehow. From my understanding of the servlet lifecycle process, each request spawns a new thread, so using FileLock would probably have no affect:

File locks are held on behalf of the entire Java virtual machine. They are not suitable for controlling access to a file by multiple threads within the same virtual machine.

(From http://docs.oracle.com/javase/7/docs/api/java/nio/channels/FileLock.html)

I guess that using the synchronized(){} keyword would also not work since I want to synchronize file system access and not access to variables/objects.

So, how can synchronize file system access in my servlet when multiple parallel requests on that servlet happen?

like image 546
Timo Ernst Avatar asked Aug 28 '14 11:08

Timo Ernst


2 Answers

I guess that using the synchronized(){} keyword would also not work since I want to synchronize file system access and not access to variables/objects.

Using synchronized can work. You are assuming that if you want to control access to object X from multiple threads, you must use synchronized on that object. You don't. You can use synchronized on any object, provided all the accesses use the same object.

In fact, it is often better to use a separate private lock object for synchronization, because it is then impossible for code outside the class to synchronize on the lock.

So , you might have something like this, with one instance for each shared file:

 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)) {
               ....
            }
         }
      }
 }
like image 101
Raedwald Avatar answered Sep 24 '22 03:09

Raedwald


You can use a Semaphore, as follows:

private static Semaphore semaphore = new Semaphore(1);

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

        // doSomeChangesTo

    } finally {
        semaphore.release();
    }
}
like image 34
troig Avatar answered Sep 21 '22 03:09

troig