java.util.concurrent.CopyOnWriteArraySet Example
In this post, we are going to discuss about the class java.util.concurrent.CopyOnWriteArraySet
and give you and idea of how you can use it on your own code when building robust multi-threaded applications.
1. CopyOnWriteArraySet Class
As the name suggests, the CopyOnWriteArraySet
class is a replacement for the standard Set
class. It uses an internal CopyOnWriteArrayList
for all of its operations. Thus, it shares the same basic properties:
- It is best suited for applications in which set sizes generally stay small, read-only operations vastly outnumber mutative operations, and you need to prevent interference among threads during traversal.
- It is thread-safe.
- Mutative operations (add, set, remove, etc.) are expensive since they usually entail copying the entire underlying array.
- Iterators do not support the mutative remove operation.
- Traversal via iterators is fast and cannot encounter interference from other threads. Iterators rely on unchanging snapshots of the array at the time the iterators were constructed.
CopyOnWriteArraySet
has been made thread-safe by the addition on copy-on-write semantics, which means that any operations that mutate the set will create a new copy of the array backing the set.
This approach to shared data is ideal when quick, consistent snapshots of data (which may occasionally be different between readers) is more important than perfect synchronization (and the attendant performance hit). This is often seen in non-mission critical data.
For a good introduction and to know more about Data Structures, Synchronized Collections and Concurrent Collections, please visit the following link:
CopyOnWriteArrayList Example
2. Executing some code
Entry.java
package com.javacodegeeks.examples.copyonwritearrayset.model; public class Entry { private String topic; private String description; public Entry() { } public Entry(String topic, String description) { this.topic = topic; this.description = description; } public String getTopic() { return topic; } public void setTopic(String topic) { this.topic = topic; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((description == null) ? 0 : description.hashCode()); result = prime * result + ((topic == null) ? 0 : topic.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Entry other = (Entry) obj; if (description == null) { if (other.description != null) return false; } else if (!description.equals(other.description)) return false; if (topic == null) { if (other.topic != null) return false; } else if (!topic.equals(other.topic)) return false; return true; } @Override public String toString() { return "Entry [topic = " + topic + ", description = " + description + "]"; } }
ForumTopicTimeline.java
package com.javacodegeeks.examples.copyonwritearrayset.runnables; import java.util.Iterator; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.TimeUnit; import com.javacodegeeks.examples.copyonwritearrayset.model.Entry; public class ForumTopicTimeline implements Runnable { public static enum Operation { ADD, REMOVE } private Entry entry; private Operation operation; private static final CopyOnWriteArraySet topics = new CopyOnWriteArraySet(); public ForumTopicTimeline() { } public ForumTopicTimeline(Entry entry, Operation operation) { this.entry = entry; this.operation = operation; } public void addEntry(Entry entry) { topics.add(entry); } public void removeEntry(Entry entry) { topics.remove(entry); } public static void printTimeline() { try { TimeUnit.SECONDS.sleep(1); System.out.println("Timeline:"); Iterator it = topics.iterator(); while (it.hasNext()) { System.out.println(it.next()); } } catch (InterruptedException ie) { ie.printStackTrace(System.err); } } public Entry getEntry() { return entry; } public void setEntry(Entry entry) { this.entry = entry; } public Operation getOperation() { return operation; } public void setOperation(Operation operation) { this.operation = operation; } public static CopyOnWriteArraySet getTopics() { return topics; } @Override public void run() { switch (this.operation) { case ADD: this.addEntry(this.entry); break; case REMOVE: this.removeEntry(this.entry); break; } } }
App.java
package com.javacodegeeks.examples.copyonwritearrayset; import java.util.Iterator; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import com.javacodegeeks.examples.copyonwritearrayset.model.Entry; import com.javacodegeeks.examples.copyonwritearrayset.runnables.ForumTopicTimeline; public class App { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(5); ForumTopicTimeline[] timeline = new ForumTopicTimeline[5]; // Create five Topics // Costly operation - A new copy of the collection is created each time timeline[0] = new ForumTopicTimeline( new Entry("Topic1", "Description1"), ForumTopicTimeline.Operation.ADD); timeline[1] = new ForumTopicTimeline( new Entry("Topic2", "Description2"), ForumTopicTimeline.Operation.ADD); timeline[2] = new ForumTopicTimeline( new Entry("Topic3", "Description3"), ForumTopicTimeline.Operation.ADD); timeline[3] = new ForumTopicTimeline( new Entry("Topic4", "Description4"), ForumTopicTimeline.Operation.ADD); timeline[4] = new ForumTopicTimeline( new Entry("Topic5", "Description5"), ForumTopicTimeline.Operation.ADD); for (int i = 0; i < 5; i++) { executorService.submit(timeline[i]); } // Print Timeline ForumTopicTimeline.printTimeline(); // Costly operation - A new copy of the collection is created each time timeline[0].setOperation(ForumTopicTimeline.Operation.REMOVE); executorService.submit(timeline[0]); // Print Timeline ForumTopicTimeline.printTimeline(); // Try to remove an Entry using the iterator Iterator it = ForumTopicTimeline.getTopics().iterator(); try { it.remove(); } catch (UnsupportedOperationException uoe) { uoe.printStackTrace(System.err); } executorService.shutdown(); } }
Let’s explain the methods used in the previous code
public boolean add(E e)
– Adds the specified element to this set if it is not already present. More formally, adds the specified element e to this set if the set contains no element e2 such that (e==null ? e2==null : e.equals(e2)). If this set already contains the element, the call leaves the set unchanged and returns false.public boolean remove(Object o)
– Removes the specified element from this set if it is present. More formally, removes an element e such that (o==null ? e==null : o.equals(e)), if this set contains such an element. Returns true if this set contained the element (or equivalently, if this set changed as a result of the call). (This set will not contain the element once the call returns.)public Iterator iterator()
– Returns an iterator over the elements contained in this set in the order in which these elements were added. The returned iterator provides a snapshot of the state of the set when the iterator was constructed. No synchronization is needed while traversing the iterator. The iterator does NOT support the remove method.
The output of the command
com.javacodegeeks.examples.copyonwritearrayset.App
should be similar to:
Timeline: Entry [topic = Topic1, description = Description1] Entry [topic = Topic2, description = Description2] Entry [topic = Topic3, description = Description3] Entry [topic = Topic4, description = Description4] Entry [topic = Topic5, description = Description5] Timeline: Entry [topic = Topic2, description = Description2] Entry [topic = Topic3, description = Description3] Entry [topic = Topic4, description = Description4] Entry [topic = Topic5, description = Description5] java.lang.UnsupportedOperationException at java.util.concurrent.CopyOnWriteArrayList$COWIterator.remove(CopyOnWriteArrayList.java:1040) at com.javacodegeeks.examples.App.main(App.java:58)
3. Download the Eclipse project of this tutorial:
This was an example of how to set use the CopyOnWriteArrayList
Class.
You can download the full source code of this example here: copyonwritearrayset.zip