1

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):

  1. THREAD 1 starts getString(). The OS starts copying into memory the bytes to be returned.
  2. THREAD 1 is stopped by the OS before finishing the copy.

  3. THREAD 2 enters the synchronized block and starts renewString(), changing the original String in memory.

  4. THREAD 1 gets control back and finish getString using a corrupted String!! 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?

Community
  • 1
  • 1

4 Answers4

3

First of all, you can remove the lock and the synchronized block and simply use:

public static synchronized String getUpdatedString(){
    if(stringNeedRenewal()){
       renewString();
    }
    return getString();
}

this synchronizes on the UpdatedString.class object.

Another thing you can do is used double-checked locking to prevent unnecessary waiting. Declare the string to be volatile and:

public static String getUpdatedString(){
    if(stringNeedRenewal()){
        synchronized(lock) {
            if(stringNeedRenewal()){
                renewString();
            }
        }
    }
    return getString();
}

Then, whether to use static or not - it seems it should be static, since you want to invoke it without any particular instance.

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
  • @Bozho, I am myself a learner.. just a small confirmation.. If we have more static methods in the class, is n't it a better idea to use the synchronization on another 'lock' object than the class itself? – peakit Mar 29 '11 at 18:07
  • Just remember that locking the class would also lock other class synchronized method invocations, even if they act on separate data – PaoloVictor Mar 29 '11 at 18:07
  • @peakit yes, @PaoloVictor answered your question :) – Bozho Mar 29 '11 at 18:09
  • Thanks for your answer @Bozho,I tried to synchronized the method, but I can see all my threads (which use different instances since they are not static) updating the String at the same time. And for the second thing, I guess it can fail in the same way as the well-known "double-checked locking" – Sarah Oneill Mar 29 '11 at 18:10
  • @Sarah Oneill a synchronized static method cannot be accessed by multiple thread. And I didn't understand your comment about the double-checked locking. – Bozho Mar 29 '11 at 18:13
  • @Bozho, thanks for the comment. Could you please explain the double-checked locking a bit more and how it prevents the unncessary waiting? – peakit Mar 29 '11 at 18:14
  • Why should the `lock` be volatile here? Shouldn't be the information on which `stringNeedRenewal` is based be volatile instead? – Paŭlo Ebermann Mar 29 '11 at 18:16
  • 1
    @peakit - you check if the string needs updating. Only if it does then you lock. But because multiple threads could have entered there, and the value could have already been changed before the current thread tries to do it - there's another check, this time guarded by the lock. – Bozho Mar 29 '11 at 18:16
  • 1
    Of course, the double checked locking is only a optimization here if the `stringNeedRenewal()` is more efficient than a lock attempt. (And it depends on how often it is used versus how often the update it really needed.) – Paŭlo Ebermann Mar 29 '11 at 18:17
  • @Bozho, +1 thanks for the answer. Might have few more doubts on this. Thanks. – peakit Mar 29 '11 at 18:18
  • @Bozho, I think the double check looks very sensible but it could not work. I think there is the same problem as with "if (helper == null) " in here:http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html. Perhaps marking the String as volatile can solve it? I do not really know. I do not know if what Paŭlo Ebermann meant is valid since I get the information from a database and I cannot mark it as "volatile". – Sarah Oneill Mar 30 '11 at 18:55
  • @Sarah Oneill what Paulo meant was addressed by my update - make the string volatile, rather than the lock itself. – Bozho Mar 30 '11 at 19:54
1

I would suggest looking into a ReentrantReadWriteLock. (Whether or not it is performant is up to you to decide.) This way you can have many read operations occur simultaneously.

Here is the example from the documentation:

 class CachedData {
   Object data;
   volatile boolean cacheValid;
   ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

   void processCachedData() {
     rwl.readLock().lock();
     if (!cacheValid) {
        // Must release read lock before acquiring write lock
        rwl.readLock().unlock();
        rwl.writeLock().lock();
        // Recheck state because another thread might have acquired
        //   write lock and changed state before we did.
        if (!cacheValid) {
          data = ...
          cacheValid = true;
        }
        // Downgrade by acquiring read lock before releasing write lock
        rwl.readLock().lock();
        rwl.writeLock().unlock(); // Unlock write, still hold read
     }

     use(data);
     rwl.readLock().unlock();
   }
 }
Jeremy
  • 22,188
  • 4
  • 68
  • 81
0

This isn't exactly what you're after, and I'm not a Java specialist, so take this with a pinch of salt :)

Perhaps the code sample you've provided is contrived, but if not, I'm unclear what the purpose of the class is. You only want one thread to update the string to it's new value. Why? Is it to save effort (because you'd rather use the processor cycles on something else)? Is it to maintain consistentcy (once a certain point is reached, the string must be updated)?

How long is the cycle between required updates?

Looking at your code...

public final class updatedString {

private static final String UPstring;
private static final Object lock = new Object();

public static String getUpdatedString(){
    synchronized(lock){
        // One thread is in this block at a time
        if(stringNeedRenewal()){
           renewString();  // This updates the shared string?
        }
    }
    // At this point, you're calling out to a method.  I don't know what the
    // method does, I'm assuming it just returns UPstring, but at this point, 
    // you're no longer synchronized.  The string actually returned may or may  
    // not be the same one that was present when the thread went through the 
    // synchronized section hence the question, what is the purpose of the
    // synchronization...
    return getString();  // This returns the shared string?
}

The right locking / optimizations depend upon the reason that you're putting them in place, the likelyhood of a write being required and as Paulo has said, the cost of the operations involved.

For some situations where writes are rare, and obviously depending upon what renewString does, it may be desirable to use an optimistic write approach. Where each thread checks if a refresh is required, proceeds to perform the update on a local and then only at the end, assigns the value across to the field being read (you need to track the age of your updates if you follow this approach). This would be faster for reading, since the check for 'does the string need renewed' can be performed outside of the synchronised section. Various other approaches could be used, depending upon the individual scenario...

forsvarir
  • 10,749
  • 6
  • 46
  • 77
  • someone deleted your last comments, do you know how I can see them again? – Sarah Oneill Mar 30 '11 at 18:53
  • @Sarah Oneill: I noticed that as well. I assumed it was you deleting your answer... I don't know how to get them back, no. Essentially, I said that I Jeremy's right, in the general case, a read/write lock is probably the most reliable approach. You don't have to worry about anything reading from the cache while it is being updated, because once a write lock has been taken out, the only thread that can take out a read lock is the one holding the write lock. – forsvarir Mar 30 '11 at 19:19
  • @Sarah Oneill: The other thing you seemed to be concerned about was that if you put 'getString' into the synchronized section, this would cause delays/concurrency issues due to the threads blocking. In reality, unless the string is HUGE, I would expect the impact of adding the string copy for return into the critical section would be negligible, compared to the more expensive database read operation which is already being performed by each thread in the synchronised block. – forsvarir Mar 30 '11 at 19:22
-1

as long as you lock is static, everything else doesn't have to be, and things will work just as they do now

iluxa
  • 6,941
  • 18
  • 36
  • i'm suggesting for your private static final Object lock = new Object(); to remain static, so you'll be locking on the same thing. Everything else doesn't have to be static. – iluxa Mar 29 '11 at 18:02
  • If the lock is static, but the rest isn't, then great you can lock access to the code block across multiple instances, but why would you want to? Assuming I'm understanding you, each instance of updatedString would have it's own copy of UPString. Wouldn't that kind of negate the need for the lock (as well as making the contents of UPString somewhat unpredictable in a multi-instance scenario)? – forsvarir Mar 29 '11 at 20:03