Java - Semaphore

[Updated: Jul 30, 2017, Created: Jul 14, 2017]

In Java, a Semaphore maintains a count (int), known as permits. This count cannot increase more than the specified maximum count. At the time of construction this count is 0.

The method Semaphore#acquire() increases the underlying count. If the count is more than the max value, this method blocks until the current value of count becomes less than or equal to max value.

The method Semaphore#release() decreases the permit count, which may potentially release a blocking acquire() call.

Semaphores are often used to restrict the number of threads that can run at the same time. These threads may be working on a shared resource or they may be working independently to achieve similar tasks.

Semaphore internally uses a synchronized queue (AbstractQueuedSynchronizer), that means it takes care of thread safety. Also release() method establishes happen-before relation with respect to the subsequent acquire() call.

Let's understand Semaphore with an example.

Example

In this example, we are going to use Swing animation to demonstrate how Semaphore works.

public class SemaphoreExample {
  private static final int WORKER_COUNT = 6;
  private static final int PERMITS = 2;

  public static void main(String[] args) throws Exception {
      List<WorkerPanel> workers = initPanels();
      Collections.shuffle(workers);
      Semaphore semaphore = new Semaphore(PERMITS);

      ExecutorService es = Executors.newFixedThreadPool(WORKER_COUNT);
      while (true) {
          for (WorkerPanel worker : workers) {
              //it will block until a permit is available
              semaphore.acquire();
              es.execute(() -> {
                  worker.work();
                  semaphore.release();
              });
          }
      }
  }

  private static List<WorkerPanel> initPanels() {
      JFrame frame = new JFrame("Permits: " + PERMITS);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.setSize(250, 200);
      frame.setLayout(new FlowLayout(FlowLayout.LEFT, 10, 10));
      List<WorkerPanel> panels = new ArrayList<>();
      for (int i = 0; i < WORKER_COUNT; i++) {
          WorkerPanel wp = new WorkerPanel();
          frame.add(wp);
          panels.add(wp);
      }

      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
      return panels;
  }
}
public class WorkerPanel extends JComponent {
  private static final int PANEL_SIZE = 60;
  private int angle = 0;
  private boolean active;

  public WorkerPanel() {
      setPreferredSize(new Dimension(PANEL_SIZE, PANEL_SIZE));
  }

  public void work() {
      active = true;
      int cycle = ThreadLocalRandom.current().nextInt(100, 200);
      for (int i = 0; i < cycle; i++) {
          angle += 5;
          if (angle >= 360) {
              angle = 0;
          }
          repaint();
          try {
              TimeUnit.MILLISECONDS.sleep(10);
          } catch (InterruptedException e) {
          }
      }
      active = false;
      repaint();
  }

  @Override
  public void paint(Graphics g) {
      g.setColor(active ? Color.BLUE : Color.LIGHT_GRAY);
      g.drawArc(0, 0, PANEL_SIZE, PANEL_SIZE, 0, 360);
      g.fillArc(0, 0, PANEL_SIZE, PANEL_SIZE, angle, 30);
  }
}

Output

As seen in the output, only two threads are allowed to work at a time, as per specified number of permits of Semaphore.

The Semaphore logic from above example can be used with some useful multiple tasks, where we want limit the number of tasks executing at a time.

Example Project

Dependencies and Technologies Used :

  • JDK 1.8
  • Maven 3.3.9

Java Semaphore Example Select All Download
  • semaphore-example
    • src
      • main
        • java
          • com
            • logicbig
              • example

See Also