I have a problem with limiting concurrent access to a method. I have a method MyService
that can be called from many places at many times. This method must return a String
, that should be updated according to some rules. For this, I have an updatedString
class. Before getting the String
, it makes sure that the String
is updated, if not, it updates it. Many threads could read the String
at the same time but ONLY ONE should renew the String
at the same time if it is out of date.
public final class updatedString {
private static final String UPstring;
private static final Object lock = new Object();
public static String getUpdatedString(){
synchronized(lock){
if(stringNeedRenewal()){
renewString();
}
}
return getString();
}
...
This works fine. If I have 7 threads getting the String, it guarantees that, if necessary, ONLY one thread is updating the String.
My question is, is it a good idea to have all this static
? Why if not? Is it fast? Is there a better way to do this?
I have read posts like this:
What Cases Require Synchronized Method Access in Java? which suggests that static mutable variables are not a good idea, and static classes either. But I cannot see any dead-lock in the code or a better valid solution. Only that some threads will have to wait until the String
is updated (if necessary) or wait for other thread to leave the synchronized block (which causes a small delay).
If the method is not static
, then I have a problem because this will not work since the synchronized method acts only for the current instance that the thread is using. Synchronized the method does not work either, it seems that the lock instance-specific and not class-specific.
The other solution could be to have a Singleton that avoids creating more than one instance and then use a single synchronized not-static class, but I do not like this solution too much.
Additional information:
stringNeedRenewal()
is not too expensive although it has to read from a database. renewString()
on the contrary is very expensive, and has to read from several tables on the database to finally come to an answer. The String
needs arbitrary renewal, but this does not happen very often (from once per hour to once per week).
@forsvarir made me think... and I think he/she was right. return getString();
MUST be inside the synchronized method. At a first sight it looks as if it can be out of it so threads will be able to read it concurrently, but what happens if a thread stops running WHILE calling getString()
and other thread partially execute renewString()
? We could have this situation (assuming a single processor):
- THREAD 1 starts
getString()
. The OS starts copying into memory the bytes to be returned. THREAD 1 is stopped by the OS before finishing the copy.
THREAD 2 enters the synchronized block and starts
renewString()
, changing the originalString
in memory.- THREAD 1 gets control back
and finish
getString
using a corruptedString
!! So it copied one part from the old string and another from the new one.
Having the read inside the synchronized block can make everything very slow, since threads could only access this one by one.
As @Jeremy Heiler pointed out, this is an abstract problem of a cache. If the cache is old, renew it. If not, use it. It is better more clear to picture the problem like this instead of a single String
(or imagine that there are 2 strings instead of one). So what happens if someone is reading at the same time as someone is modifying the cache?