Parallel map with weak keys

I have a very parallel application that uses resources in the file system. The chances that two threads will have access to the same resource at the same time are pretty small, but if that happens, the application will most likely show wired behavior.

Each resource can be mapped to a String coordinate vector (complete with the ResourceIdentifier class). In my current solution, I created a ConcurrentMap of resource identifiers for collecting monitors that are used by threads to access a resource: ( ResourceIdentifier overrides equals and hashCode correctly.)

 ConcurrentMap<ResourceIdentifier, ResourceIdentifier> concurrentMap = new ConcurrentHashMap<>(); public Object aquireMonitor(ResourceIdentifier resourceIdentifier) { concurrentMap.putIfAbsent(resourceIdentifier, resourceIdentifier); return concurrentMap.get(resourceIdentifier); } 

When accessing a resource, I synchronize access to the monitor object that is returned by aquireMonitor . As far as I understand the ConcurrentHashMap implementation, this does not necessarily block all threads (I read this blog article to understand the implementation.), And my application can happily work without the risk of simultaneously accessing one of the resources that previously introduced ugly errors in very rare cases.

However: my application manages a lot of resources, and ConcurrentMap grows with runtime. This is why I am now trying to add weak reference semantics to my application (using Guava):

 ConcurrentMap<ResourceIdentifier, ResourceIdentifier> concurrentMap = new MapBuilder().weakValues().weakKeys() .concurrencyLevel(CONCURRENCY_LEVEL).makeMap(); public Object aquireMonitor(ResourceIdentifier resourceIdentifier) { ResourceIdentifier monitor; do { concurrentMap.putIfAbsent(resourceIdentifier, resourceIdentifier); monitor = concurrentMap.get(resourceIdentifier); } while(monitor == null); return monitor; } 

CONCURRENCY_LEVEL is, of course, a static field.

My thought was this: whenever a monitor is still being used by another thread, it will of course contain a (strong) link to that monitor. Therefore, the entry in ConcurrentMap will not be garbage collected, and it is guaranteed that the monitor will be used if two threads want to access the same resource. (The loop addresses a possible garbage collection between putIfAbsent and get calls.)

However, MapMaker.weakKeys violates the contract that the entries are found by equals and instead uses an identifier.

Now I wonder: Does anyone know where to go from here? Or is such an approach a bad idea? And as a side question: would the entire record be deleted from the card if I only used weakValues ? Or will the card always have another strong link to its keys? Thanks for the help!

PS: My first assumption is that I should go from card to cache. Perhaps this is the best solution? I had never used Guava before, but at the moment I found the same restriction on comparing keys for caches.

PPS: I cannot create locks on the file system. (Not my call.)

+4
source share
2 answers

You will need weak references for keys and values.

I recommend that you switch to cache or deny that you switch to ConcurrentMap with SoftReferences - GC is impatient about collecting weak links and, therefore, they are not suitable for the cache, while it delays the collection of soft links, but does not allow them to raise OutOfMemoryError . To implement a parallel map with soft links, you will create a ConcurrentMap wrapper, for example.

 class SoftConcurrentMap<K, V> extends ConcurrentHashMap<SoftReference<K>, SoftReference<V>> { ConcurrentHashMap<SoftReference<K>, SoftReference<V>> map = new ConcurrentHashMap<>(); V public void get(Object key) { SoftReference<V> value = map.get(new SoftRefrence(key)); if(value != null && value.get() != null) { return value.get(); } else { map.remove(new SoftReference(key)); return null; } } V put(K key, V value) { SoftReference<V> oldValue = map.put(new SoftReference(key), new SoftReference(value)); return oldValue == null ? null : oldValue.get(); } } 

And so on. These are a lot of wrapping methods, so I recommend using EHCache instead.

+4
source

I found another neat solution that I really implemented. It might have been too easy to just think about it at the beginning:

 ConcurrentMap<ResourceIdentifier, ResourceIdentifier> concurrentMap = new MapBuilder().weakValues() .concurrencyLevel(CONCURRENCY_LEVEL).makeMap(); public Object aquireMonitor(ResourceIdentifier resourceIdentifier) { ResourceIdentifier monitor; do { concurrentMap.putIfAbsent(resourceIdentifier, new Object()); monitor = concurrentMap.get(resourceIdentifier); } while(monitor == null); return monitor; } 

without using weakKeys . It works like a charm. The keys no longer constitute a strong reference to the actual object that is used as the monitor, and the entries in the card receive garbage collected whenever the thread no longer refers to them.

0
source

Source: https://habr.com/ru/post/1489071/


All Articles