Collections.newSetFromMap(»ConcurrentHashMap«) vs. Collections.synchronizedSet(»HashSet«)

11,354

Solution 1

What you may be thinking of is

Set<Type> set = Collections.newSetFromMap(new ConcurrentHashMap<Type, Boolean>());

This supports concurrent updates and reads. Its Iterator won't throw ConcurrentModicationException. where as

Set<Type> set = Collections.synchronizedSet(new HashSet<Type());

Is more light weight but only allows one thread at a time to access the set. You need to lock the set explicitly if you want to Iterator over it and you can still get a CME if you don't update it in a safe way (while iterating over it)

Solution 2

The first one returns a Set that basically has the same thread-safe and performance guarantees as the map passed as argument. If the map isn't thread-safe, the set won't be either. You typically use this method to create a concurrent set from a concurrent map, because there is no ConcurrentHashSet in the API.

The second one returns a proxy to the given set, which has all its methods synchronized.

Solution 3

Actually you may get several Set thread-safe implementations.

I. Collections.synchronizedSet(new HashSet

I would not recommend this solution. It is itself thread-safe, must still be used with care in a concurrent environment. See:

Stack stack = new SynchronizedArrayStack(new ArrayStack());
...
// don't do this in a multi-threaded environment
if (!stack.isEmpty()) {
  stack.pop();              // can throw IllegalStateException
}

As a result you must use client-side locking:

synchronized(stack) {
  if (!stack.isEmpty()) {
    stack.pop();
  }
}

II. The second alternative concurrent implementation of Set interface - CopyOnWriteArraySet. However this solution should not be used in a context where you were expecting many searches or insertions. But iteration costs O(1) per element faster than HashSet and it has one advantage which is really compelling in some applications.

III. The last one uses the implementation of CuncurrentHashMap:

Collections.newSetFromMap(new ConcurrentHashMap<Type, Boolean>());

It uses implementations of java.util.concurrent.locks.Lock. The map divides itself into parts that can be separately locked, giving improved concurrency. So you should choose between the last two options.

Thera are also options for sorted set impelementations. I would recommend you to read Java Generics and Collections chapter 11.5 Collections and Thread Safety.

Solution 4

The Collections API leave the construction of the map to the client as below:

ConcurrentHashMap<String, Boolean> conMap = new   ConcurrentHashMap<String, Boolean>();
Set<String> users = Collections.newSetFromMap(conMap);
System.out.println("Users: " + users);
users.add("Jon");//results in adding to the conMap instance
users.add("Tyron");
System.out.println("Users: " + users);
System.out.println("conMap = " + conMap);

Adding to the set also adds to the map

Users: [Tyron, Jon] conMap = {Tyron=true, Jon=true}

conMap.put("Jubin", Boolean.FALSE);
System.out.println("Users: " + users);
System.out.println("conMap = " + conMap);

Adding to the Map also results on adding to the Set

Users: [Tyron, Jubin, Jon] conMap = {Tyron=true, Jubin=false, Jon=true}

ConcurrentHashMap.newKeySet creates new HashMap with the KeySetView

ConcurrentHashMap.KeySetView<String, Boolean> keySetView = ConcurrentHashMap.newKeySet();
keySetView.add("Feba");
System.out.println("keySetView = " + keySetView);
System.out.println("keySetView.getMap() = " + keySetView.getMap());

keySetView = [Feba] keySetView.getMap() = {Feba=true}

keySetView.getMap().put("BeN",Boolean.TRUE);
System.out.println("keySetView = " + keySetView);
System.out.println("keySetView.getMap() = " + keySetView.getMap());

keySetView = [BeN, Feba] keySetView.getMap() = {BeN=true, Feba=true}

Share:
11,354
fbahr
Author by

fbahr

Updated on June 03, 2022

Comments

  • fbahr
    fbahr almost 2 years

    Apparently, there are two ways to obtain a thread-safe HashSet instance using Java’s Collections utility class.

    I ask:

    • How do they differ?
    • Which, and under what circumstances, is to be preferred over the other?
  • fbahr
    fbahr almost 12 years
    Duh, my bad. First of all: I meant to write Collections.newSetFromMap(»ConcurrentHashMap«) - so, the question at hand: is the set obtained from newSetFromMap(...), using a map from java.util.concurrent, still thread-safe?
  • user207421
    user207421 almost 12 years
    @fbahr As I said, check the Javadoc. It contains the answer.
  • user207421
    user207421 almost 12 years
    I think you should reword 'only allows one thread to be reading or writing'.
  • David Bosschaert
    David Bosschaert about 9 years
    ... and the Javadoc says 'The resulting set displays the same ordering, concurrency, and performance characteristics as the backing map' so if you use it with a concurrent map, the set is also a concurrent set.
  • Ivan Voroshilin
    Ivan Voroshilin about 9 years
    Very sad, that guys who got up-votes answered incorrectly. David's questions is correct.