CopyOnWriteArraySet

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.

Tip
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.

Download
You can download the full source code of this example here: copyonwritearrayset.zip

Armando Flores

Armando graduated from from Electronics Engineer in the The Public University Of Puebla (BUAP). He also has a Masters degree in Computer Sciences from CINVESTAV. He has been using the Java language for Web Development for over a decade. He has been involved in a large number of projects focused on "ad-hoc" Web Application based on Java EE and Spring Framework.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button