Synchronizers Core Java

The java.util.concurrent package contains several classes that help manage a set of collaborating threads—see Table below. These mechanisms have “canned functionality” for common rendezvous patterns between threads. If you have a set of collaborating threads that follows one of these behavior patterns, you should simply reuse the appropriate library class instead of trying to come up with a handcrafted collection of locks and conditions.

Synchronizers

SynchronizersSynchronizers

Semaphores

Conceptually, a semaphore manages a number of permits. To proceed past the semaphore, a thread requests a permit by calling acquire. Only a fixed number of permits are available, limiting the number of threads that are allowed to pass. Other threads may issue permits by calling release. There are no actual permit objects. The semaphore simply keeps a count. Moreover, a permit doesn’t have to be released by the thread that acquires it. In fact, any thread can issue any number of permits. If it issues more than themaximum available, the semaphore is simply set to the maximum count. This generalitymakes semaphores both very flexible and potentially confusing.

Semaphores were invented by Edsger Dijkstra in 1968, for use as a synchronization primitive. Dijkstra showed that semaphores can be efficiently implemented and that they are powerful enough to solve many common thread synchronization problems. In just about any operating systems textbook, you will find implementations of bounded queues using semaphores. Of course, application programmers shouldn’t reinvent bounded queues. We suggest that you only use semaphores when their behavior maps well onto your synchronization problem, without your going through mental contortions.

One simple example is a semaphore with a permit count of 1. Such a semaphore can be used as a gate for one thread that is opened and closed by another thread. In the section “Example: Pausing and Resuming an Animation”, you will see an example in which a worker thread produces an animation. Occasionally, the worker thread waits for the user to press a button. The worker thread tries to acquire a permit, and it has to wait until the button click causes a permit to be issued.

Countdown Latches

A CountDownLatch lets a set of threads wait until a count has reached zero. The countdown latch is one-time only. Once the count has reached 0, you cannot increment it again. A useful special case is a latch with a count of 1. This implements a one-time gate.

Threads are held at the gate until another thread sets the count to 0. Imagine, for example, a set of threads that need some initial data to do their work. The worker threads are started and wait at the gate. Another thread prepares the data. When it is ready, it calls countDown, and all worker threads proceed.

You can then use a second latch to check when all worker threads are done. Initialize the latch with the number of threads. Each worker thread counts down that latch just before it terminates. Another thread that harvests the work results waits on the latch, and it proceeds as soon as all workers have terminated.

Barriers

The CyclicBarrier class implements a rendezvous called a barrier. Consider a number of threads that are working on parts of a computation. When all parts are ready, the results need to be combined. When a thread is done with its part, we let it run against the barrier.

Once all threads have reached the barrier, the barrier gives way and the threads can proceed. Here are the details. First, construct a barrier, giving the number of participating threads:

The await method takes an optional timeout parameter:

If any of the threads waiting for the barrier leaves the barrier, then the barrier breaks. (A thread can leave because it called await with a timeout or because it was interrupted.) In that case, the await method for all other threads throws a BrokenBarrierException. Threads that are already waiting have their await call terminated immediately.

You can supply an optional barrier action that is executed when all threads have reached the barrier:


The action can harvest the result of the individual threads.

The barrier is called cyclic because it can be reused after all waiting threads have been released. In this regard, it differs from a CountDownLatch, which can only be used once.

Exchangers

An Exchanger is used when two threads are working on two instances of the same data buffer. Typically, one thread fills the buffer, and the other consumes its contents. When both are done, they exchange their buffers.

Synchronous Queues

A synchronous queue is a mechanism that pairs up producer and consumer threads. When a thread calls put on a SynchronousQueue, it blocks until another thread calls take, and vice versa. Unlike the case with an Exchanger, data are only transferred in one direction, from the producer to the consumer.

Even though the SynchronousQueue class implements the BlockingQueue interface, it is not conceptually a queue. It does not contain any elements—its size method always returns 0.

Consider a program that does some work, updates the screen display, then waits for the user to look at the result and press a button to continue, and then does the next unit of work. A semaphore with a permit count of 1 can be used to synchronize the worker thread and the event dispatch thread. The worker thread calls acquire whenever it is ready to pause.

The GUI thread calls release whenever the user clicks the Continue button. What happens if the user clicks the button multiple times while the worker thread is ready? Because only one permit is available, the permit count stays at 1.

The program in Listing below puts this idea to work. The program animates a sorting algorithm. A worker thread sorts an array, stopping periodically and waiting for the user to give permission to proceed. The user can admire a painting of the current state of the algorithm and press the Continue button to allow the worker thread to go to the next step.

We didn’t want to bore you with the code for a sorting algorithm, so we simply call Arrays.sort, which implements the merge sort algorithm. To pause the algorithm, we supply a Comparator object that waits for the semaphore. Thus, the animation is paused wheneverthe algorithm compares two elements. We paint the current values of the array andhighlight the elements that are being compared (see Figure below).

NOTE: The animation shows the merging of smaller sorted ranges into larger ones, but it is not entirely accurate. The mergesort algorithm uses a second array for holding temporary values that we do not get to see. The point of this example is not to delve into sorting algorithms, but to show how to use a semaphore for pausing a worker thread.

Animating a sort algorithm

Animating a sort algorithm

java.util.concurrent.CyclicBarrier

  • CyclicBarrier(int parties)
  • CyclicBarrier(int parties, Runnable barrierAction)
    constructs a cyclic barrier for the given number of parties. The barrierAction is executed when all parties have called await on the barrier.
  • int await()
  • int await(long time, TimeUnit unit)
    waits until all parties have called await on the barrier or until the timeout has been reached, in which case a TimeoutException is thrown. Upon success, returns the arrival index of this party. The first party has index parties - 1, and the last party has index 0.

java.util.concurrent.CountDownLatch

  • CountdownLatch(int count)
    constructs a countdown latch with the given count.
  • void await()
    waits for this latch to count down to 0.
  • boolean await(long time, TimeUnit unit)
    waits for this latch to count down to 0 or for the timeout to elapse. Returns true if the count is 0, false if the timeout elapsed.
  • public void countDown()
    counts down the counter of this latch.

java.util.concurrent.Exchanger<V>

  • V exchange(V item)
  • V exchange(V item, long time, TimeUnit unit)
    blocks until another thread calls this method, and then exchanges the item with the other thread and returns the other thread’s item. The second method throws a TimeoutException after the timeout has elapsed.

java.util.concurrent.SynchronousQueue<V>

  • SynchronousQueue()
  • SynchronousQueue(boolean fair)
    constructs a synchronous queue that allows threads to hand off items. If fair is true, the queue favors the longest-waiting threads.
  • void put(V item)
    blocks until another thread calls take to take this item.
  • V take()
    blocks until another thread calls put. Returns the item that the other thread provided.

java.util.concurrent.Semaphore

  • Semaphore(int permits)
  • Semaphore(int permits, boolean fair)
    constructs a semaphore with the given maximum number of permits. If fair is true, the queue favors the longest-waiting threads.
  • void acquire()
    waits to acquire a permit.
  • boolean tryAcquire()
    tries to acquire a permit; returns false if none is available.
  • boolean tryAcquire(long time, TimeUnit unit)
    tries to acquire a permit within the given time; returns false if none is available.
  • void release()
    releases a permit.


Face Book Twitter Google Plus Instagram Youtube Linkedin Myspace Pinterest Soundcloud Wikipedia

All rights reserved © 2018 Wisdom IT Services India Pvt. Ltd DMCA.com Protection Status

Core Java Topics