Threadsafe way to expose keySet ()

This should be a fairly common occurrence when I have a card and you want to safely copy your keyring set:

public MyClass { Map<String,String> map = // ... public final Set<String> keys() { // returns key set } } 

Now, if my "card" is not thread safe, it is unsafe:

  public final Set<String> keys() { return map.keySet(); } 

And no:

  public final Set<String> keys() { return Collections.unmodifiableSet(map.keySet()); } 

Therefore, I need to create a copy, for example:

  public final Set<String> keys() { return new HashSet(map.keySet()); } 

However, this does not look safe, as this constructor bypasses the parameter elements and adds () s them. Thus, while this copying is in progress, a ConcurrentModificationException may occur.

So then:

  public final Set<String> keys() { synchronized(map) { return new HashSet(map.keySet()); } } 

seems like a solution. Does this look right?

+4
source share
5 answers

This solution is not particularly useful if you do not plan to also synchronize it on the map wherever it is used. Syncing on it does not prevent someone else from invoking methods at the same time. This only prevents them from synchronizing it.

The best solution, apparently, is to simply use ConcurrentHashMap in the first place, if you know that you need parallel puts and deletes, while someone can iterate over. If the concurrency behavior this class offers is not what you need, you probably just need to use a fully synchronized map.

+4
source
Good question. I would use the Google Guava library. More specifically, the com.google.common.collect.ImmutableSet.copyOf(Collection<? extends E>) method com.google.common.collect.ImmutableSet.copyOf(Collection<? extends E>) . The documentation said that this method is thread safe.
+2
source

Another option is to use ConcurrentHashMap. Its keySet () is thread safe, so there is no need to synchronize or take a copy.

0
source

If you are interested in a thread-safe iterator with an accurate snapshot of elements through the iteration process, then go to the following.

 public class ThreadSafeIteratorConcurrentMap { private ConcurrentMap<String, String> itrSafeMap = null; public ThreadSafeIteratorConcurrentCollection() { itrSafeMap = new ConcurrentHashMap<String, String> } public void synchronized put(psConference conference, String p_key) { itrSafeMap.putIfAbsent(p_key, conference); } public psConference getConference(String p_key) { return (itrSafeMap.get(p_key)); } public void synchronized remove(String p_key) { itrSafeMap.remove(p_key); } public boolean containsKey(String p_key) { return itrSafeMap.containsKey(p_key); } // Get the size of the itrSafeMap. public int size() { return itrSafeMap.size(); } public Iterator<String> valueIterator() { return (itrSafeMap.values().iterator()); } public Iterator<String> keyIterator() { return (itrSafeMap.keySet().iterator()); } } 

Then, when you need a thread safe iterator with an accurate snapshot of the elements; then use it in the synchronized block as shown below.

  synchronized(threadSafeIteratorConcurrentMapObject) { Iterator<String> keyItr = threadSafeIteratorConcurrentMapObject.keyIterator(); while(keyItr.hasNext()){ // Do whatever } } 

If you do not mind modifying the collection during the iteration; only focus on the snapshot of the elements during the creation of the iterator; then without a synchronization block you can use keyItr. Which is already thread safe; this happens through a ConcurrentModificationException.

0
source

You can create a temporary map using Collections.UnmodifiableMap , and then iterate over the key set.

-1
source

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


All Articles