Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Synchronizing on an Integer value [duplicate]

Possible Duplicate:
What is the best way to increase number of locks in java

Suppose I want to lock based on an integer id value. In this case, there's a function that pulls a value from a cache and does a fairly expensive retrieve/store into the cache if the value isn't there.

The existing code isn't synchronized and could potentially trigger multiple retrieve/store operations:

//psuedocode public Page getPage (Integer id){    Page p = cache.get(id);    if (p==null)    {       p=getFromDataBase(id);       cache.store(p);    } } 

What I'd like to do is synchronize the retrieve on the id, e.g.

   if (p==null)    {        synchronized (id)        {         ..retrieve, store        }    } 

Unfortunately this won't work because 2 separate calls can have the same Integer id value but a different Integer object, so they won't share the lock, and no synchronization will happen.

Is there a simple way of insuring that you have the same Integer instance? For example, will this work:

 syncrhonized (Integer.valueOf(id.intValue())){ 

The javadoc for Integer.valueOf() seems to imply that you're likely to get the same instance, but that doesn't look like a guarantee:

Returns a Integer instance representing the specified int value. If a new Integer instance is not required, this method should generally be used in preference to the constructor Integer(int), as this method is likely to yield significantly better space and time performance by caching frequently requested values.

So, any suggestions on how to get an Integer instance that's guaranteed to be the same, other than the more elaborate solutions like keeping a WeakHashMap of Lock objects keyed to the int? (nothing wrong with that, it just seems like there must be an obvious one-liner than I'm missing).

like image 326
Steve B. Avatar asked Mar 18 '09 20:03

Steve B.


People also ask

Can we synchronize a variable?

You can have both static synchronized method and nonstatic synchronized method and synchronized blocks in Java but we can not have synchronized variable in java. Using synchronized keyword with a variable is illegal and will result in compilation error.

What is synchronization with example?

The process of allowing only a single thread to access the shared data or resource at a particular point of time is known as Synchronization. This helps us to protect the data from the access by multiple threads. Java provides the mechanism of synchronization using the synchronized blocks.

What is multithreading Synchronisation?

Synchronization in java is the capability to control the access of multiple threads to any shared resource. In the Multithreading concept, multiple threads try to access the shared resources at a time to produce inconsistent results. The synchronization is necessary for reliable communication between threads.


1 Answers

You really don't want to synchronize on an Integer, since you don't have control over what instances are the same and what instances are different. Java just doesn't provide such a facility (unless you're using Integers in a small range) that is dependable across different JVMs. If you really must synchronize on an Integer, then you need to keep a Map or Set of Integer so you can guarantee that you're getting the exact instance you want.

Better would be to create a new object, perhaps stored in a HashMap that is keyed by the Integer, to synchronize on. Something like this:

public Page getPage(Integer id) {   Page p = cache.get(id);   if (p == null) {     synchronized (getCacheSyncObject(id)) {       p = getFromDataBase(id);       cache.store(p);     }   } }  private ConcurrentMap<Integer, Integer> locks = new ConcurrentHashMap<Integer, Integer>();  private Object getCacheSyncObject(final Integer id) {   locks.putIfAbsent(id, id);   return locks.get(id); } 

To explain this code, it uses ConcurrentMap, which allows use of putIfAbsent. You could do this:

  locks.putIfAbsent(id, new Object()); 

but then you incur the (small) cost of creating an Object for each access. To avoid that, I just save the Integer itself in the Map. What does this achieve? Why is this any different from just using the Integer itself?

When you do a get() from a Map, the keys are compared with equals() (or at least the method used is the equivalent of using equals()). Two different Integer instances of the same value will be equal to each other. Thus, you can pass any number of different Integer instances of "new Integer(5)" as the parameter to getCacheSyncObject and you will always get back only the very first instance that was passed in that contained that value.

There are reasons why you may not want to synchronize on Integer ... you can get into deadlocks if multiple threads are synchronizing on Integer objects and are thus unwittingly using the same locks when they want to use different locks. You can fix this risk by using the

  locks.putIfAbsent(id, new Object()); 

version and thus incurring a (very) small cost to each access to the cache. Doing this, you guarantee that this class will be doing its synchronization on an object that no other class will be synchronizing on. Always a Good Thing.

like image 83
Eddie Avatar answered Sep 27 '22 19:09

Eddie