This week's book giveaway is in the Servlets forum.
We're giving away four copies of Murach's Java Servlets and JSP and have Joel Murach on-line!
See this thread for details.
The moose likes Java in General and the fly likes Does this help prevent writing to a common file at the same time ? Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of Murach's Java Servlets and JSP this week in the Servlets forum!
JavaRanch » Java Forums » Java » Java in General
Bookmark "Does this help prevent writing to a common file at the same time ?" Watch "Does this help prevent writing to a common file at the same time ?" New topic
Author

Does this help prevent writing to a common file at the same time ?

Artemesia Lakener
Ranch Hand

Joined: Jun 21, 2005
Posts: 162
Suppose we have a file "WriteToLog.java" which is commonly called by many other applications at run time and mostprobably those applications call this file simultaneously often. In this "WriteToLog.java", it has a method as follows --

public void writeLog() {
// open a log file "log"
// write to "log" file
}

Since all the applications will call WriteLog.java at the same time, we don't want they write to this "log" at the same time. So do you think changing it to

public synchronized void writeLog() {

...
}

can solve the problem simply ?
Ronnie Ho
Ranch Hand

Joined: Aug 10, 2005
Posts: 47
Are many instances of this class being created? If yes, then the solution will not work because the synchronized writeLog() is trying to get the lock of an instance and there can be many instances of this class, so many instances can write to the "log" file at the same time. A simple solution can be

which gets the lock of the static variable that all instances of this class share.
Tony Morris
Ranch Hand

Joined: Sep 24, 2003
Posts: 1608

So do you think changing it to


can solve the problem simply ?


Certainly not. This will prevent concurrent accesses to the writeLog() method of a particular object. All you need is two instances and the barrier becomes superfluous.

What you might want to look at is the FileChannel.lock() method, the FileLock class and all the associated 'gotchyas'.
[ October 25, 2005: Message edited by: Tony Morris ]

Tony Morris
Java Q&A (FAQ, Trivia)
Jim Yingst
Wanderer
Sheriff

Joined: Jan 30, 2000
Posts: 18671
Among those "gotchyas": FileChannel.lock() will do nothing to prevent multiple threads in the same JVM from accessing the file using two different instances. (I.e. it's irrelevant to the problem Tony just cited.) From the API:
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.

What the file locks do do is, they prevent access from other processes (assuming the operating system enforces files locks, which is not necessarily guaranteed, but this is the best hope we have of enforcing it). So using a file lock you can maybe prevent access from someone editing the file with notepad, or from a second JVM process (someone types "java MyClass" again).

To prevent concurrent access from other threads in the same JVM though, Ronnie's suggestion of synchronizing on a static object is more appropriate. There are other possibilities too - for example if WriteToLog is a singleton, then synchronizing the writeLog() method was sufficient. Or perhaps there can be multiple WriteToLog instances in the JVM, but each one is guaranteed to be associated with a different log file. (Which probably implies a static Map<File, WriteToLog> which is used to maintain this association.) Then synchronization of the writeLog() is sufficient since you don't care if other threads write to different files, but you need to prevent them from writing to the same file. This sort of thing is tricky though, so unless you know what you are doing it's probably better to just use Ronnie's suggestion.

A robust application should probably guard against both access from other threads in the same JVM process (using Ronnie's suggestion) and against access from other processes (using file locking as Tony suggested). It's not clear which is more important for this particular problem (are you concernted about multiple threads, or multiple processes, or both?) but the prudent thing is to protect against both.
[ October 26, 2005: Message edited by: Jim Yingst ]

"I'm not back." - Bill Harding, Twister
Tony Morris
Ranch Hand

Joined: Sep 24, 2003
Posts: 1608

To prevent concurrent access from other threads in the same JVM though, Ronnie's suggestion of synchronizing on a static object is more appropriate.

This will prevent concurrent access from within a particular class loader (the scope of a Class instance that is associated with the monitor).

From the J2SE API Specification, "File channels are safe for use by multiple concurrent threads." Confused? So you should be. This is the nature of the J2SE API Specification (and implicit through stating requires in an ambiguous language such as English). The requirements are also stated in Java (less ambiguous) by the reference implementation (the Sun Java implementation), so given all the suggestions you've had so far, I suggest testing them against said RI and drawing some solid conclusions.
Jim Yingst
Wanderer
Sheriff

Joined: Jan 30, 2000
Posts: 18671
[Tony]: This will prevent concurrent access from within a particular class loader

Mmm yes, this is a good caveat to add.

I suggest testing them against said RI and drawing some solid conclusions.

Interesting. On Windows 2000 Pro at least, two separate threads do no seem to be able to get a FileLock on the same file simultaneously - the second thread is forced to wait. So it does appear that at least one implementation from Sun does provide more thread-safety enforcement than is explicitly guaranteed in the API. This doesn't seem to actually contradict the API, in the sense that the API doesn't say an implementation must allow two different threads concurrent access to the file lock in this situation. Rather, the API seems to be deliberately nonspecific in this area.

The other system I have ready access to right now is a Mac - the same test there shows that two threads in the same JVM can simultaneously acquire a lock on the same file. Of course the JVM here came from Apple, not Sun - but it looks like the behavior is perfectly legal according to the API.

So I retract my previous statement that lock() will do nothing to prevent concurrent access from different threads in the same JVM. Tony is correct to suggest that FileChannel.lock() may help in preventing concurrent access, at least on some platforms. If you're writing a system that only needs to work on a certain platform (or set of platforms) and you can test the behavior carefully, this technique could be exactly what you need. On the other hand if you're writing a system that needs to be able to run on many different platforms, it would IMO be a mistake to rely too much on FileChannel to provide all the enforcement you need. Though it can be a part of a multilayered defense against concurrent access as I alluded previously. Probably no absolute guarantees are possible across all platforms, but a multilayered defense may be "good enough" (as good as we can get, anyway).
[ October 26, 2005: Message edited by: Jim Yingst ]
Artemesia Lakener
Ranch Hand

Joined: Jun 21, 2005
Posts: 162
please llet me 'modify' the issue a little bit here --- Suppose this java class is called by Servlet, for example, I have

public doPost(request, response) {

// call the common java class, instantiate an instance of it, and write to the log file

}

Then do I even have to put any "synchronized" key words in my java class ? I guess not. Because the instance is created inside the doPost() method (local variable). And servlet spawns a new thread every time, so the instance is created inside the thread only, plus servlet handles muttiple threading issue, so I guess I don't have to worry about anything in my java class, right ?
Artemesia Lakener
Ranch Hand

Joined: Jun 21, 2005
Posts: 162
Originally posted by Ronnie Ho:
Are many instances of this class being created? If yes, then the solution will not work because the synchronized writeLog() is trying to get the lock of an instance and there can be many instances of this class, so many instances can write to the "log" file at the same time. A simple solution can be

which gets the lock of the static variable that all instances of this class share.



What if I use "Singleton" to create only ONE global instance of this class ? then I only add "synchronized" to public synchronized void writeLog() ? Does this wrok ?
Ken Blair
Ranch Hand

Joined: Jul 15, 2003
Posts: 1078
This is a follow-up to my previous post about "thread safe and writing to a common file". I changed the code to include singleton. Here is sample

public class LogWriter {

private static LogWriter lw = new LogWriter();
private LogWriter() {}
public static synchronized LogWriter getLogWriter() {
if(lw == null) lw = new LogWriter();
return lw;
}

private synchronized writeLog() {
// open file
// write file
// close file
}
}


Does this work ? I guess in this case I don't need to put "synchronized" keyword for an "object" inside the "writeLog()" method, right ? This is because I ensure only one static global instance is created. Please help me verify it.

thanks lot.


Either synchronize the getInstance() method and use lazy instantiation or initialize it when the class is loaded. There's no point in doing both, one cancels out the other.



Pick one. The first initializes it when the class is loaded and is probably what you want. No synchronization on getLogWriter() is necessary because only one instance will ever be created. The second initializes it the first time it's requested, synchronization is necessary so multiple instances are not created by a thread be preempted after passing the null check but before creating it.
[ October 26, 2005: Message edited by: Ken Blair ]
Mr. C Lamont Gilbert
Ranch Hand

Joined: Oct 05, 2001
Posts: 1170

Ken is correct. Also note that if you dont keep a reference to the class with the static variable somewhere, that class can get garbage collected. Though it shouldnt affect your program except a small speed bump. Unless you are hovering at the limit of memory.

In any event, you definitely do NOT want to open close files with reckless abandon. You can possibly run out of file handles if too many hits come. Search the forums for complaints about folks rapidly using files.
Tony Morris
Ranch Hand

Joined: Sep 24, 2003
Posts: 1608

Also note that if you dont keep a reference to the class with the static variable somewhere

You don't go around keeping references to classes do you? Why duplicate what a class loader does? When do you think a class loader releases its class(es) for garbage collection? (hint: never).

You might mean, you must keep a handle on the class loader that loaded the class, otherwise, the "singleton" disappears. Note that a singleton cannot exist, assuming that our universe is infinite. One might define a singleton within a given context, such as a class loader (as has been demonstrated with a static reference), or within a JVM (using for example a class loader registry), or several JVMs, or... but never across the entire universe (assumptions withstanding). Assumptions not withstanding, it is terribly impractical and would probably cost lots and lots of money.

final class TheExpensiveSingleton
 
 
subject: Does this help prevent writing to a common file at the same time ?
 
Similar Threads
Singleton and writing to a common file
Appending the data in frame or popup window
Java using httpunit and junit
log method of servlet context
calling java file from jsp