Your code will potentially call veryCostlyOperation (name) several times. The problem is that after viewing the map there is an unsynchronized step:
public void request(String name) { Resource resource = resources.get(name); if (resource == null) { synchronized(this) {
The get () function from the map is synchronized across the map, but checking the result for null is not protected by anything. If several threads enter this request with the same "name", they will all see a zero result from resources.get () until one of them completes an expensive operation and puts the resource on the resource map.
A simpler and more working, but less scalable approach should be to go with a normal map and make the entire request method synchronized. If in practice, in practice, there is no problem, I would choose a simple approach.
For higher scalability, you can fix your code by checking the card again after synchronization (this) to catch the case described above. It still would not provide better scalability, since synchronized (this) allows only one thread to perform expensive work, while in many practical cases you want to prevent multiple executions for the same resource, while allowing simultaneous requests to different resources. In this case, you will need some means to synchronize on the requested resource. A very simple example:
private static class ResourceEntry { public Resource resource; } private Map<String, ResourceEntry> resources = new HashMap<String, ResourceEntry>(); public Resource request(String name) { ResourceEntry entry; synchronized (resources) { entry = resources.get(name); if (entry == null) {
This is just a rough sketch. Basically, it performs a synchronized ResourceEntry search and then synchronizes with a ResourceEntry to ensure that a specific resource will be created only once.
Durandal
source share