cool breeze
cool breeze

Reputation: 4811

Why is a semaphore used when using synchronization?

I was reading about semaphore's and in the code example it confused me why a semaphore was used when the code uses sychronization around the method that is ultimately called. Isn't that doing the same thing, i.e. restricting 1 thread at a time to perform the mutation?

class Pool {
   private static final int MAX_AVAILABLE = 100;
   private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);

   public Object getItem() throws InterruptedException {
     available.acquire();
     return getNextAvailableItem();
   }

   public void putItem(Object x) {
     if (markAsUnused(x))
       available.release();
   }

   // Not a particularly efficient data structure; just for demo

   protected Object[] items = ... whatever kinds of items being managed
   protected boolean[] used = new boolean[MAX_AVAILABLE];

   protected synchronized Object getNextAvailableItem() {
     for (int i = 0; i < MAX_AVAILABLE; ++i) {
       if (!used[i]) {
          used[i] = true;
          return items[i];
       }
     }
     return null; // not reached
   }

   protected synchronized boolean markAsUnused(Object item) {
     for (int i = 0; i < MAX_AVAILABLE; ++i) {
       if (item == items[i]) {
          if (used[i]) {
            used[i] = false;
            return true;
          } else
            return false;
       }
     }
     return false;
   }
 }

I'm referring to the call to getItem() which calls acquire(), and then calls getNextAvailableItem, but that is synchronized anyhow.

What am I missing?

Reference: http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Semaphore.html

Upvotes: 2

Views: 1148

Answers (2)

Chris K
Chris K

Reputation: 11927

The semaphore and the synchronized block are doing two different jobs.

  • The synchronized keyword is protecting getNextAvailableItem() when it is accessing and mutating the array of items. An operation that would corrupt if it was not restricted to one thread at a time.

  • The semaphore will allow up to 100 threads through, significantly more than 1. Its purpose in this code sample is to block requests for an object from the pool when the pool is empty, and to then unblock one thread when an object is returned to the pool. Without the semaphore, things would look like they were working until the pool was empty. At that time requesting threads would not block and wait for an object to be returned, but would instead receive null.

Upvotes: 5

OldCurmudgeon
OldCurmudgeon

Reputation: 65811

A Semaphore gives you a thread-safe counter that blocks when the acquire has been called beyond the initial limit. release can be used to undo an acquire.

It will guarantee that if a call to acquire succeeds there is sufficient capacity to hold the new item.

In the sample there are loops that look for a free item. Using a Semaphore ensures that none of those loops are begun until there is a free item.

synchronized only guarantees that ony one thread can execute this section of code at a time.

Upvotes: 4

Related Questions