jacekn
jacekn

Reputation: 1541

How to manipulate file thread-safely in Java?

Every time a page is requested on my website, I log some details with a service call like this:

logService.logPageRequest(session, request, contentId);

This service call makes use of a static FileUtils (my own) method to do the actual update on the file.

public static void appendToTextFile(String string, String fileRootDirectory, String subdirectory, String filename, boolean startOnNewLine) throws Exception {
    // code irrelevant to the question removed

    String filePath=fileDirectory+"/"+filename;
    File file=new File(filePath);
    if(file.exists()) {
        FileWriter out = new FileWriter(filePath, true);
        if(startOnNewLine) {
            out.write("\r\n");
        }
        out.write(string);
        out.close();
     } else {
         FileWriter out = new FileWriter(filePath);
         out.write(string);
         out.close();
}

Every so often (let's say scheduled every 15 minutes), I execute another service which renames this file and starts processing it.

I'd like to understand how I can go about ensuring that both concurrent writes are safe and rename is not executed while a write is in the process. I guess the answer will be some form of synchronization.

Upvotes: 1

Views: 3180

Answers (1)

Pavel Zdenek
Pavel Zdenek

Reputation: 7278

The two services need to share a synchronization object: it can be anything (even new byte[1]) but it must be the one and same instance, passed into your "services". Whenever there is a critical section, which means part of a code which wants to operate on a shared resource, it needs to be wrapped in

synchronized(theInstanceOfSynchronizationObject) {
  // ... your code for critical section
}

The rule of good locking is to know deterministically what are you locking, what is exactly the shared resource. What will kill you (and introduce deadlocks) is random adding of synchronized blocks in hope of "solving". In your particular example, it's most probably the actions on file: just actions, not the operations on filename strings, neither the instantiation of File : you can have many File objects pointing to the same file on disk and it's not a critical operation, as long as it does not touch the disk yet. So i would say

File file=new File(filePath);
synchronized(theInstanceOfSynchronizationObject) {
  if(file.exists()) {
  } else {
  }
}

Similarly, protect the File actions in your other service. Keep in mind that it's not File object you are protecting, but the file itself.

Upvotes: 2

Related Questions