Untangling Java concurrency

Java 101: Java concurrency without the pain, Part 1

Get started with the Java Concurrency Utilities

1 2 3 4 Page 4
Page 4 of 4

Listing 8. PhaserDemo.java

import java.util.ArrayList;
import java.util.List;

import java.util.concurrent.Phaser;

public class PhaserDemo
{
   public static void main(String[] args)
   {
      List<Runnable> tasks = new ArrayList<>();
      tasks.add(new Runnable()
                {
                   @Override
                   public void run()
                   {
                      System.out.printf("%s running at %d%n",
                                        Thread.currentThread().getName(),
                                        System.currentTimeMillis());
                   }
                });
      tasks.add(new Runnable()
                {
                   @Override
                   public void run()
                   {
                      System.out.printf("%s running at %d%n",
                                        Thread.currentThread().getName(),
                                        System.currentTimeMillis());
                   }
                });
      runTasks(tasks);
   }

   static void runTasks(List<Runnable> tasks)
   {
      final Phaser phaser = new Phaser(1); // "1" to register self
      // create and start threads
      for (final Runnable task: tasks)
      {
         phaser.register();
         new Thread()
         {
            @Override
            public void run()
            {
               try
               {
                  Thread.sleep(50+(int)(Math.random()*300));
               }
               catch (InterruptedException ie)
               {
                  System.out.println("interrupted thread");
               }
               phaser.arriveAndAwaitAdvance(); // await all creation
               task.run();
            }
         }.start();
      }

      // allow threads to start and deregister self
      phaser.arriveAndDeregister();
   }
}

Listing 8 is based on the first code example in Phaser's Javadoc. This example shows how to use Phaser instead of CountDownLatch to control a one-shot action serving a variable number of threads.

The application creates a pair of runnable tasks that each report the time (in milliseconds relative to the Unix epoch) at which its starts to run. Compile and run this application, and you should observe output that's similar to the following:

Thread-0 running at 1366315297635
Thread-1 running at 1366315297635

As you would expect from countdown latch behavior, both threads start running at (in this case) the same time even though a thread may have been delayed by as much as 349 milliseconds thanks to the presence of Thread.sleep().

Comment out phaser.arriveAndAwaitAdvance(); // await all creation and you should now observe the threads starting at radically different times, as illustrated below:

Thread-1 running at 1366315428871
Thread-0 running at 1366315429100

Concurrent collections

The Java Collections framework provides interfaces and classes in the java.util package that facilitate working with collections of objects. Interfaces include List, Map, and Set. Classes include ArrayList, Vector, Hashtable, HashMap, and TreeSet.

Collections classes such as Vector and Hashtable are thread-safe. You can make other classes (like ArrayList) thread-safe by using synchronized wrapper factory methods such as Collections.synchronizedMap(), Collections.synchronizedList(), and Collections.synchronizedSet().

There are a couple of problems with the thread-safe collections:

  1. Code that iterates over a collection that might be modified by another thread during the iteration requires a lock to avoid a thrown java.util.ConcurrentModificationException. This requirement is necessary because Collections framework classes return fail-fast iterators, which are iterators that throw ConcurrentModificationException when a collection is modified during iteration. Fail-fast iterators are often an inconvenience to concurrent applications.
  2. Performance often suffers when these synchronized collections are accessed frequently from multiple threads; this is a performance problem that impacts an application's scalability.

The Java Concurrency Utilities framework overcomes these problems by introducing performant and highly-scalable collections-oriented types, which are part of java.util.concurrent. These collections-oriented classes return weakly consistent iterators, which have the following properties:

  • When an element is removed after iteration starts, but hasn't yet been returned via the iterator's next() method, it won't be returned.
  • When an element is added after iteration starts, it may or may not be returned.
  • Regardless of changes to the collection, no element is returned more than once in an iteration.

The following list summarizes the Java Concurrency Utilities framework's collection-oriented types:

  • BlockingDeque<E>: This interface extends BlockingQueue and java.util.Deque to describe a double-ended queue with additional support for blocking operations that wait for the deque to become non-empty when retrieving an element, and wait for space to become available in the deque when storing an element.
  • BlockingQueue<E>: This interface extends java.util.Queue to describe a queue with additional support for operations that wait for the queue to become non-empty when retrieving an element, and wait for space to become available in the queue when storing an element.
  • ConcurrentMap<K, V>: This interface extends java.util.Map to describe a map with additional atomic putIfAbsent, remove, and replace methods.
  • ConcurrentNavigableMap<K, V>: This interface extends ConcurrentMap and java.util.NavigableMap to describe a concurrent map with navigable operations.
  • TransferQueue<E>: This interface extends BlockingQueue to describe a blocking queue in which producers may wait for consumers to receive elements.
  • ArrayBlockingQueue<E>: This class describes a bounded blocking queue backed by an array.
  • ConcurrentHashMap<K, V>: This class describes a hash table supporting full concurrency of retrievals and adjustable expected concurrency for updates.
  • ConcurrentLinkedDeque<E>: This class describes an unbounded thread-safe deque based on linked nodes.
  • ConcurrentLinkedQueue<E>: This class describes an unbounded thread-safe queue based on linked nodes.
  • ConcurrentSkipListMap<K, V>: This class describes a scalable concurrent ConcurrentNavigableMap implementation.
  • ConcurrentSkipListSet<E>: This class describes a scalable concurrent java.util.NavigableSet implementation based on a ConcurrentSkipListMap.
  • CopyOnWriteArrayList<E>: This class describes a thread-safe variant of ArrayList in which all mutative operations (e.g., add and set) are implemented by making a fresh copy of the underlying array whenever an element is added or removed. However, in-progress iterations continue to work on the previous copy (when the iterator was created). Although there's some cost to copying the array, this cost is acceptable in situations where there are many more iterations than modifications.
  • CopyOnWriteArraySet<E>: This class describes a Set that uses an internal CopyOnWriteArrayList for all of its operations.
  • DelayQueue<E extends Delayed>: This class describes an unbounded blocking queue of java.util.concurrent.Delayed elements, in which an element can only be taken when its delay has expired. (Delayed is an interface for marking objects that should be acted upon after a given delay.)
  • LinkedBlockingDeque<E>: This class describes an optionally-bounded blocking deque based on linked nodes.
  • LinkedBlockingQueue<E>: This class describes an optionally-bounded blocking queue based on linked nodes.
  • LinkedTransferQueue<E>: This class describes an unbounded transfer queue based on linked nodes.
  • PriorityBlockingQueue<E>: This class describes an unbounded blocking queue that uses the same ordering rules as java.util.PriorityQueue and supplies blocking retrieval operations.
  • SynchronousQueue<E>: This class describes a blocking queue in which each insert operation must wait for a corresponding remove operation by another thread, and vice versa.

Working with Concurrent Collections

For an example of what you can do with Concurrent Collections, consider CopyOnWriteArrayList, as demonstrated in Listing 9.

Listing 9. CopyOnWriteArrayListDemo.java

import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.List;

import java.util.concurrent.CopyOnWriteArrayList;

public class CopyOnWriteArrayListDemo
{
   public static void main(String[] args)
   {
      List<String> empList = new ArrayList<>();
      empList.add("John Doe");
      empList.add("Jane Doe");
      empList.add("Rita Smith");
      Iterator<String> empIter = empList.iterator();
      while (empIter.hasNext())
         try
         {
            System.out.println(empIter.next());
            if (!empList.contains("Tom Smith"))
               empList.add("Tom Smith");
         }
         catch (ConcurrentModificationException cme)
         {
            System.err.println("attempt to modify list during iteration");
            break;
         }

      List<String> empList2 = new CopyOnWriteArrayList<>();
      empList2.add("John Doe");
      empList2.add("Jane Doe");
      empList2.add("Rita Smith");
      empIter = empList2.iterator();
      while (empIter.hasNext())
      {
         System.out.println(empIter.next());
         if (!empList2.contains("Tom Smith"))
            empList2.add("Tom Smith");
      }

   }
}

Listing 9 contrasts CopyOnWriteArrayListDemo with ArrayList from a ConcurrentModificationException perspective. During each iteration, an attempt is made to add a new employee name to the list. The ArrayList iteration fails with this exception, whereas the CopyOnWriteArrayList iteration ignores the addition.

If you compile and run this application you should see the following output:

John Doe
attempt to modify list during iteration
John Doe
Jane Doe
Rita Smith

In conclusion

The Java Concurrency Utilities framework offers a high-level alternative to Java's low-level threading capabilities. This library's thread-safe and high-performant types were designed to be used as the building blocks in concurrent classes and applications.

The Java Concurrency Utilities framework is organized into several smaller frameworks, with types stored in java.util.concurrent and two subpackages, java.util.concurrent.atomic and java.util.concurrent.locks.

In this article, I introduced three of these frameworks: the Executor framework, synchronizers, and the Java Concurrent Collections. You can learn more about them by exploring the exercises in this article's source code file. In Part 2 we'll explore locks, atomic variables, Fork/Join, and more.

Jeff Friesen is a freelance tutor and software developer with an emphasis on Java and Android. In addition to writing Java and Android books for Apress, Jeff has written numerous articles on Java and other technologies for JavaWorld, informIT, Java.net, DevSource, and SitePoint. Jeff can be contacted via his website at TutorTutor.ca.

Learn more about this topic

Using the Java Concurrency Utilities -- more tutorials on JavaWorld:

  • Modern threading for not-quite-beginners (Cameron Laird, JavaWorld, January 2013): Get an overview of callable and runnable, learn more about synchronized blocks, and find out how you can use java.util.concurrent to work around deadlock and similar threading pitfalls.
  • Multicore processing for client-side Java applications (Kirill Grouchnikov, JavaWorld, September 2007): Get a hands-on introduction to collection sorting using the CountDownLatch and Executors.newFixedThreadPool concurrency utilities.
  • Java concurrency with thread gates (Obi Ezechukwu, JavaWorld, March 2009): See the Java concurrency utilities at work in a realistic implementation of the Thread Gates concurrency pattern.
  • Hyper-threaded Java (Randall Scarberry, JavaWorld, November 2006): See for yourself how two java.util.concurrent classes were used to optimize thread use for faster performance in a real-world application.
  • Java Tip 144: When to use ForkJoinPool vs ExecutorService (Madalin Ilie, JavaWorld, October 2011): Demonstrates the performance impact of replacing the standard ExecutorService class with ForkJoinPool in a web crawler.

Popular articles in the Java 101 series

1 2 3 4 Page 4
Page 4 of 4