I've got a web application where people ask for resources. This resources are cached using a synchronized hash map for efficiency. The problem here is when two different requests come for the same uncached resource at the same time: the operation retrieving the resources takes up a lot of memory, so I want to avoid calling it more than once for the same resource.
Can somebody please tell me if there is any potential problem with the following snippet? Thanks in advance.
private Map<String, Resource> resources = Collections.synchronizedMap(new HashMap<String, Resource>());
public void request(String name) {
Resource resource = resources.get(name);
if (resource == null) {
synchronized(this) {
if (resources.get(name) == null) {
resource = veryCostlyOperation(name); // This should only be invoked once per resource...
resources.put(resource);
} else {
resource = resources.get(name);
}
}
}
...
}
Synchronization means controlling the access of multiple threads to any shared resource. A synchronized resource can be accessed by only one thread at a time. HashMap can be synchronized using the Collections.
An additional problem, beyond that of impedance mismatch, can occur because the cache used by an application may not be tightly integrated with an RDBMS. This problem is referred to as cache synchronization. It occurs after the relational data has been transformed into an object format placed in an application cache.
HashMap is non-synchronized. It is not thread-safe and can't be shared between many threads without proper synchronization code whereas Hashtable is synchronized. It is thread-safe and can be shared with many threads.
2. Synchronized HashMap: Java HashMap is a non-synchronized collection class. If we need to perform thread-safe operations on it then we must need to synchronize it explicitly. The synchronizedMap() method of java.
One possible problem is that you create unnecessary contention by executing veryCostlyOperation()
inside a synchronized
block, so that many threads cannot retrieve their (independent) resources at the same time. This can be solved by using Future<Resource>
as values of the map:
Map<String, Future<Resource>> map = new ConcurrentHashMap<String, Future<Resource>>();
...
Future<Resource> r = map.get(name);
if (r == null) {
FutureTask task = null;
synchronized (lock) {
r = map.get(name);
if (r == null) {
task = new FutureTask(new Callable<Resource>() {
public Resource call() {
return veryCostlyOperation(name);
}
});
r = task;
map.put(name, r);
}
}
if (task != null) task.run(); // Retrieve the resource
}
return r.get(); // Wait while other thread is retrieving the resource if necessary
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With