Java Thread Tutorial - Java Synchronizers








A synchronizer object is used with a set of threads.

It maintains a state, and depending on its state, it lets a thread pass through or forces it to wait.

This section will discuss four types of synchronizers:

  • Semaphores
  • Barriers
  • Latches
  • Exchangers




Semaphores

A semaphore is used to control the number of threads that can access a resource.

The Semaphore class in the java.util.concurrent package represents the semaphore synchronizer.

You create a semaphore using one of its constructors, like so:

final int MAX_PERMITS  = 3;
Semaphore  s = new Semaphores(MAX_PERMITS);

Another constructor for the Semaphore class takes fairness as the second argument as in

final int MAX_PERMITS  = 3;
Semaphore  s = new Semaphores(MAX_PERMITS,  true); // A  fair  semaphore

If you create a fair semaphore, in the situation of multiple threads asking for permits, the semaphore will guarantee first in, first out (FIFO). That is, the thread that asked for the permit first will get the permit first.

To acquire a permit, use the acquire() method.

It returns immediately if a permit is available.

It blocks if a permit is not available. The thread can be interrupted while it is waiting for the permit to become available.

Other methods of the Semaphore class let you acquire one or multiple permits in one go. To release a permit, use the release() method.

The following code shows a Restaurant Class, which Uses a Semaphore to Control Access to Tables.

import java.util.Random;
import java.util.concurrent.Semaphore;
//from  www. j  a  va  2 s .co m
class Restaurant {
  private Semaphore tables;

  public Restaurant(int tablesCount) {
    this.tables = new Semaphore(tablesCount);
  }

  public void getTable(int customerID) {
    try {
      System.out.println("Customer  #" + customerID
          + "  is trying  to  get  a  table.");
      tables.acquire();
      System.out.println("Customer #" + customerID + "  got  a  table.");
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }

  public void returnTable(int customerID) {
    System.out.println("Customer #" + customerID + "  returned a  table.");
    tables.release();
  }
}
class RestaurantCustomer extends Thread {
  private Restaurant r;
  private int customerID;
  private static final Random random = new Random();

  public RestaurantCustomer(Restaurant r, int customerID) {
    this.r = r;
    this.customerID = customerID;
  }
  public void run() {
    r.getTable(this.customerID); // Get a table
    try {
      int eatingTime = random.nextInt(30) + 1;
      System.out.println("Customer #" + this.customerID
          + "  will eat for " + eatingTime + "  seconds.");
      Thread.sleep(eatingTime * 1000);
      System.out.println("Customer #" + this.customerID
          + "  is done  eating.");
    } catch (InterruptedException e) {
      e.printStackTrace();
    } finally {
      r.returnTable(this.customerID);
    }
  }
}
public class Main{
  public static void main(String[] args) {
    Restaurant restaurant = new Restaurant(2);
    for (int i = 1; i <= 5; i++) {
      RestaurantCustomer c = new RestaurantCustomer(restaurant, i);
      c.start();
    }
  }
}

The code above generates the following result.





Barriers

A barrier makes a group of threads meet at a barrier point.

A thread from a group arriving at the barrier waits until all threads in that group arrive.

Once the last thread from the group arrives at the barrier, all threads in the group are released.

You can use a barrier when you have a task that can be divided into subtasks; each subtask can be performed in a separate thread and each thread must meet at a common point to combine their results.

The CyclicBarrier class in the java.util.concurrent package provides the implementation of the barrier synchronizer.

CyclicBarrier class can be reused by calling its reset() method.

The following code shows how to Use a CyclicBarrier in a Program.

import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/*from  w w  w. j a v  a 2  s.  c  o m*/
class Worker extends Thread {
  private CyclicBarrier barrier;
  private int ID;
  private static Random random = new Random();

  public Worker(int ID, CyclicBarrier barrier) {
    this.ID = ID;
    this.barrier = barrier;
  }
  public void run() {
    try {
      int workTime = random.nextInt(30) + 1;
      System.out.println("Thread #" + ID + " is going to work for " + workTime + "  seconds");
      Thread.sleep(workTime * 1000);
      System.out.println("Thread #" + ID + " is waiting at the barrier.");
      this.barrier.await();
      System.out.println("Thread #" + ID + " passed the barrier.");
    } catch (InterruptedException e) {
      e.printStackTrace();
    } catch (BrokenBarrierException e) {
      System.out.println("Barrier is broken.");
    }
  }

}
public class Main {
  public static void main(String[] args) {
    Runnable barrierAction = () -> System.out.println("We are ready.");
    CyclicBarrier barrier = new CyclicBarrier(3, barrierAction);
    for (int i = 1; i <= 3; i++) {
      Worker t = new Worker(i, barrier);
      t.start();
    }
  }
}

The code above generates the following result.

Phasers

A Phaser provides functionality similar to the CyclicBarrier and CountDownLatch synchronizers. It provides the following features:

Phaser is reusable.

The number of parties to synchronize on a Phaser can change dynamically. In a CyclicBarrier, the number of parties is fixed at the time the barrier is created.

A Phaser has an associated phase number, which starts at zero. When all registered parties arrive at a Phaser, the Phaser advances to the next phase and the phase number is incremented by one. The maximum value of the phase number is Integer.MAX_VALUE. After its maximum value, the phase number restarts at zero.

A Phaser has a termination state. All synchronization methods called on a Phaser in a termination state return immediately without waiting for an advance.

A Phaser has three types of parties count: a registered parties count, an arrived parties count, and an unarrived parties count.

The registered parties count is the number of parties that are registered for synchronization. The arrived parties count is the number of parties that have arrived at the current phase of the phaser.

The unarrived parties count is the number of parties that have not yet arrived at the current phase of the phaser.

When the last party arrives, the phaser advances to the next phase.

Optionally, a Phaser lets you execute a phaser action when all registered parties arrive at the phaser.

CyclicBarrier lets you execute a barrier action, which is a Runnable task.

We specify a phaser action by writing code in the onAdvance() method of your Phaser class.

We need to inherit from the Phaser class and override the onAdvance() method to provide a Phaser action.

The following code shows how to represent Tasks That Start Together by Synchronizing on a Phaser

import java.util.Random;
import java.util.concurrent.Phaser;
/*  ww w . j  av a 2  s .  c o  m*/
class StartTogetherTask extends Thread {
  private Phaser phaser;
  private String taskName;
  private static Random rand = new Random();

  public StartTogetherTask(String taskName, Phaser phaser) {
    this.taskName = taskName;
    this.phaser = phaser;
  }

  @Override
  public void run() {
    System.out.println(taskName + ":Initializing...");
    int sleepTime = rand.nextInt(5) + 1;
    try {
      Thread.sleep(sleepTime * 1000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println(taskName + ":Initialized...");
    phaser.arriveAndAwaitAdvance();
    System.out.println(taskName + ":Started...");
  }
}

public class Main {
  public static void main(String[] args) {
    Phaser phaser = new Phaser(1);
    for (int i = 1; i <= 3; i++) {
      phaser.register();
      String taskName = "Task  #" + i;
      StartTogetherTask task = new StartTogetherTask(taskName, phaser);
      task.start();
    }
    phaser.arriveAndDeregister();
  }
}

The code above generates the following result.

Example

The following code shows how to add a Phaser Action to a Phaser.

import java.util.concurrent.Phaser;
//w  w  w  .  j a  va2 s.  c  om
public class Main {
  public static void main(String[] args) {
    Phaser phaser = new Phaser() {
      protected boolean onAdvance(int phase, int parties) {
        System.out.println("Inside onAdvance(): phase  = " + phase
            + ",  Registered Parties = " + parties);
        // Do not terminate the phaser by returning false
        return false;
      }
    };
    // Register the self (the "main" thread) as a party 
    phaser.register();
    System.out.println("#1: isTerminated():" + phaser.isTerminated());
    phaser.arriveAndDeregister();

    // Trigger another phase advance
    phaser.register();
    phaser.arriveAndDeregister();

    System.out.println("#2: isTerminated():" + phaser.isTerminated());
    phaser.forceTermination();
    System.out.println("#3: isTerminated():" + phaser.isTerminated());
  }
}

The code above generates the following result.

Example 2

The following code shows how to use a Phaser to Generate Some Integers.

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Phaser;
//from w  w  w .j a  va 2 s . c  o  m
class AdderTask extends Thread {
  private Phaser phaser;
  private String taskName;
  private List<Integer> list;

  public AdderTask(String taskName, Phaser phaser, List<Integer> list) {
    this.taskName = taskName;
    this.phaser = phaser;
    this.list = list;
  }

  @Override
  public void run() {
    do {
      System.out.println(taskName + "  added  " + 3);
      list.add(3);
      phaser.arriveAndAwaitAdvance();
    } while (!phaser.isTerminated());
  }
}

public class Main {
  public static void main(String[] args) {
    final int PHASE_COUNT = 2;
    Phaser phaser = new Phaser() {
      public boolean onAdvance(int phase, int parties) {
        System.out.println("Phase:" + phase + ", Parties:" + parties
            + ",  Arrived:" + this.getArrivedParties());
        boolean terminatePhaser = false;
        if (phase >= PHASE_COUNT - 1 || parties == 0) {
          terminatePhaser = true;
        }

        return terminatePhaser;
      }
    };
    List<Integer> list = Collections.synchronizedList(new ArrayList<Integer>());
    int ADDER_COUNT = 3;
    phaser.bulkRegister(ADDER_COUNT + 1);
    for (int i = 1; i <= ADDER_COUNT; i++) {
      String taskName = "Task  #" + i;
      AdderTask task = new AdderTask(taskName, phaser, list);
      task.start();
    }
    while (!phaser.isTerminated()) {
      phaser.arriveAndAwaitAdvance();
    }
    int sum = 0;
    for (Integer num : list) {
      sum = sum + num;
    }
    System.out.println("Sum = " + sum);
  }
}

The code above generates the following result.

Latches

A latch makes a group of threads wait until it reaches its terminal state.

Once a latch reaches its terminal state, it lets all threads pass through.

Unlike a barrier, it is a one-time object. It cannot be reset and reused.

A latch is used where a number of activities cannot proceed until a certain number of one-time activities have completed.

For example, a service should not start until all services that it depends on have started.

The CountDownLatch class in the java.util.concurrent package provides the implementation of a latch.

import java.util.concurrent.CountDownLatch;
class LatchHelperService extends Thread {
  private int ID;
  private CountDownLatch latch;
  public LatchHelperService(int ID, CountDownLatch latch) {
    this.ID = ID;/*w ww . ja v a2s .c  om*/
    this.latch = latch;
  }
  public void run() {
    try {
      Thread.sleep(1000);
      System.out.println("Service #" + ID + "  has  started...");
    } catch (InterruptedException e) {
      e.printStackTrace();
    } finally {
      this.latch.countDown();
    }
  }
}

class LatchMainService extends Thread {
  private CountDownLatch latch;

  public LatchMainService(CountDownLatch latch) {
    this.latch = latch;
  }
  public void run() {
    try {
      System.out.println("waiting for services to start.");
      latch.await();
      System.out.println("started.");
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

public class Main {
  public static void main(String[] args) {
    // Create a countdown latch with 2 as its counter
    CountDownLatch latch = new CountDownLatch(2);
    LatchMainService ms = new LatchMainService(latch);
    ms.start();
    for (int i = 1; i <= 2; i++) {
      LatchHelperService lhs = new LatchHelperService(i, latch);
      lhs.start();
    }
  }
}

The code above generates the following result.

Exchangers

An exchanger lets two threads wait for each other at a synchronization point.

When both threads arrive, they exchange an object and continue their activities.

The Exchanger class provides an implementation for an exchanger synchronizer.

The following code shows a Producer Thread That Will Use an Exchanger to Exchange Data with a Consumer.

import java.util.ArrayList;
import java.util.concurrent.Exchanger;
/*w  w  w . j a  v  a 2 s . com*/
class ExchangerProducer extends Thread {
  private Exchanger<ArrayList<Integer>> exchanger;
  private ArrayList<Integer> buffer = new ArrayList<Integer>();
  public ExchangerProducer(Exchanger<ArrayList<Integer>> exchanger) {
    this.exchanger = exchanger;
  }

  public void run() {
    while (true) {
      try {
        System.out.println("Producer.");
        Thread.sleep(1000);
        fillBuffer();
        System.out.println("Producer has produced and waiting:" + buffer);
        buffer = exchanger.exchange(buffer);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }

  public void fillBuffer() {
    for (int i = 0; i <= 3; i++) {
      buffer.add(i);
    }
  }
}

class ExchangerConsumer extends Thread {
  private Exchanger<ArrayList<Integer>> exchanger;
  private ArrayList<Integer> buffer = new ArrayList<Integer>();
  public ExchangerConsumer(Exchanger<ArrayList<Integer>> exchanger) {
    this.exchanger = exchanger;
  }

  public void run() {
    while (true) {
      try {
        System.out.println("Consumer.");
        buffer = exchanger.exchange(buffer);
        System.out.println("Consumer  has received:" + buffer);
        Thread.sleep(1000);
        System.out.println("eating:"+buffer);
        buffer.clear();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }  
}
public class Main {
  public static void main(String[] args) {
    Exchanger<ArrayList<Integer>> exchanger = new Exchanger<>();
    ExchangerProducer producer = new ExchangerProducer(exchanger);
    ExchangerConsumer consumer = new ExchangerConsumer(exchanger);
    producer.start();
    consumer.start();
  }
}

The code above generates the following result.