This post shows how to synchronize HashSet in Java and the HashSet’s thread safe alternative which can be used instead of a HashSet.
HashSet is not thread safe
HashSet in Java is not thread safe as it is not synchronized by default. If you are using HashSet in a multi-threaded environment where it is accessed by multiple threads concurrently and structurally modified too by even a single thread then it must be synchronized externally. A structural modification is defined as any operation that adds or deletes one or more elements, or explicitly resizes the backing array; merely setting the value of an element is not a structural modification.
Options for thread-safe Set
If you want to synchronize HashSet in Java or looking for a thread-safe alternative of HashSet then there are following options.
- Use Collections.synchronizedSet() to synchronize Set- This method returns a synchronized (thread-safe) set backed by the specified set.
- Using CopyOnWriteArraySet- Another option is to use CopyOnWriteArraySet from the java.util.concurrent package which is the thread safe implementation of the Set. CopyOnWriteArraySet uses an internal CopyOnWriteArrayList for all of its operations. For all mutative operations (add, set, remove, etc.) a new copy is created that is why using it is expensive.
Using Collections.synchronizedSet()
You can synchronize HashSet by using Collections.synchronizedSet() method. First we’ll see an example what happens if HashSet is used in a multi-threaded environment without synchronizing it.
In the Java code four threads are created, each of these thread adds 5 elements to the Set. After all the threads are done Set size should be 20.
public class SetSynchro implements Runnable{ private Set<String> numSet; public SetSynchro(Set<String> numSet){ this.numSet = numSet; } public static void main(String[] args) { Set<String> numSet = new HashSet<String>(); /// 4 threads Thread t1 = new Thread(new SetSynchro(numSet)); Thread t2 = new Thread(new SetSynchro(numSet)); Thread t3 = new Thread(new SetSynchro(numSet)); Thread t4 = new Thread(new SetSynchro(numSet)); t1.start(); t2.start(); t3.start(); t4.start(); try { t1.join(); t2.join(); t3.join(); t4.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Size of Set is " + numSet.size()); } @Override public void run() { System.out.println("in run method" + Thread.currentThread().getName()); String str = Thread.currentThread().getName(); for(int i = 0; i < 5; i++){ // adding thread name to make element unique numSet.add(i + str); try { // delay to verify thread interference Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }Output
in run methodThread-2 in run methodThread-0 in run methodThread-3 in run methodThread-1 Size of Set is 19
In one of the run size was 19, in another run 18 and sometimes even 20, so you can see that thread interference is making the behavior unpredictable. So we’ll synchronize the HashSet using the same example.
public class SetSynchro implements Runnable{ private Set<String> numSet; public SetSynchro(Set<String> numSet){ this.numSet = numSet; } public static void main(String[] args) { // Synchronized Set Set<String> numSet = Collections.synchronizedSet(new HashSet<String>()); /// 4 threads Thread t1 = new Thread(new SetSynchro(numSet)); Thread t2 = new Thread(new SetSynchro(numSet)); Thread t3 = new Thread(new SetSynchro(numSet)); Thread t4 = new Thread(new SetSynchro(numSet)); t1.start(); t2.start(); t3.start(); t4.start(); try { t1.join(); t2.join(); t3.join(); t4.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Size of Set is " + numSet.size()); } @Override public void run() { System.out.println("in run method" + Thread.currentThread().getName()); String str = Thread.currentThread().getName(); for(int i = 0; i < 5; i++){ // adding thread name to make element unique numSet.add(i + str); try { // delay to verify thread interference Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }Output
in run methodThread-3 in run methodThread-2 in run methodThread-1 in run methodThread-0 Size of Set is 20
Now every time size of HashSet is 20.
Using CopyOnWriteArraySet
Another option to have a thread safe Set is to use CopyOnWriteArraySet. Let’s see it using a simple example where a CopyOnWriteArraySet is created and then iterated. While iteration an element is removed using the Set’s remove method, it still won’t throw ConcurrentModificationException. In the output you can see iteration is displaying all the elements as iteration is done on a separate copy of CopyOnWriteArraySet.
import java.util.Iterator; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; public class SetItr { public static void main(String[] args) { Set<String> carSet = new CopyOnWriteArraySet<String>(); carSet.add("Audi"); carSet.add("Jaguar"); carSet.add("BMW"); carSet.add("Mini Cooper"); Iterator<String> i = carSet.iterator(); while (i.hasNext()){ carSet.remove("Jaguar"); System.out.println(i.next()); } System.out.println("Set after removal" + carSet); } }Output
Audi Jaguar BMW Mini Cooper Set after removal[Audi, BMW, Mini Cooper]
That's all for the topic How to Synchronize Java HashSet. If something is missing or you have something to share about the topic please write a comment.
You may also like
- How to Synchronize Java HashMap
- How to Synchronize Java ArrayList
- Java ListIterator With Examples
- Race Condition in Java With Examples
- Java String valueOf() Method With Examples
- Java Program to Convert Numbers to Words
- How to Improve Map-Reduce Performance in Hadoop
- Spring Boot Microservices Example
No comments:
Post a Comment