Friday, September 27, 2013

Semaphore in Java : Concurrency

In this post we'll see one of the synchronizers that is used in  java.util.concurrent library i.e, Semaphore. We'll see a simplest and easiest way to use Semaphore so that it is easy to remember and understand easily.

What is Semaphore?

A semaphore controls access to shared resources. A semaphore maintains a counter to specify the number of resources that the semaphore controls.
Access to the resource is allowed if the counter is greater than zero,while a zero value of the counter indicates that no resource is available at the moment and so the access is denied. Semaphore: n-member access to a resource

A semaphore acts as a limiter of available resource pool depth; for example, a semaphore with a capacity of 10 allows a maximum of 10 threads to acquire it at once, and any further threads that attempt to acquire it will block until one of the other threads releases it.

This is somewhat different from ordinary mutual exclusion or monitor locking, which is typically used to prevent more than one thread from simultaneously modifying the same variables and causing inconsistent results or program state.

Synchronization vs Semaphore
Synchronized allows only one thread of execution to access the resource at the same time. Semaphore allows up to n threads of execution to access the resource at the same time.

Basically two method that used are for acquiring and releasing resources from a semaphore
  • acquire() : decrement the value
  • release() : increment the value

If a thread calls acquire() and the counter is zero (i.e., resources are unavailable), the thread waits until the counter is non-zero and then gets the resource for use. Once the thread is done using the resource, it calls release() to increment the resource availability counter.

Note if the number of resources is 1, then at a given time only one thread can access the resource; in this case, using the semaphore is similar to using a lock

A semaphore initialized to one, and which is used such that it only has at most one permit available, can serve as a mutual exclusion lock.This is more commonly known as a binary semaphore, because it only has two states.
Semaphores are often used to restrict the number of threads than can access some (physical or logical) resource.

  • Semaphore(int permits) : This method create Semaphore objects with a given number of permits, here permits means the number of threads that can access the resource at a time.
  • void acquire() : Acquires a permit if available; otherwise, it blocks until a permit becomes available
  • void release() : Releases a permit from the semaphore basically decrement the value

Example First we see example of binary semaphore.To show how to use it, we are going to implement a print queue that can be used by concurrent tasks to print their jobs. This print queue will be protected by a binary semaphore, so only one thread can print at a time.

How it works...
The key to this example is in the printJob() method of the PrintQueue class. This method shows the three steps you must follow when you use a semaphore to implement a critical section, and protect the access to a shared resource:

  • First, you acquire the semaphore, with the acquire() method.
  • Then, you do the necessary operations with the shared resource.
  •  Finally, release the semaphore with the release() method.

Another important point in this example is the constructor of the PrintQueue class and the initialization of the Semaphore object. You pass the value 1 as the parameter of this constructor, so you are creating a binary semaphore. The initial value of the internal counter is 1, so you will protect the access to one shared resource, in this case, the print queue.

The Semaphore class has two additional versions of the acquire() method:

  • acquireUninterruptibly(): The acquire() method; when the internal counter of the semaphore is 0, blocks the thread until the semaphore is released. During this blocked time, the thread may be interrupted and then this method throws an InterruptedException exception. This version of the acquire operation ignores the interruption of the thread and doesn't throw any exceptions.
  • tryAcquire(): This method tries to acquire the semaphore. If it can, the method returns the true value. But if it can't, the method returns the false value instead of being blocked and waits for the release of the semaphore. It's your responsibility to take the correct action based on the return value.

Fairness in semaphores
The concept of fairness is used by the Java language in all classes that can have various threads blocked waiting for the release of a synchronization resource (for example, a semaphore). The default mode is called the non-fair mode. In this mode, when the synchronization resource is released, one of the waiting threads is selected to get this resource, but it's selected without any criteria. The fair mode changes this behavior and forces to select the thread that has been waiting for more time.
As occurs with other classes, the Semaphore class admits a second parameter in its constructor. This parameter must take a Boolean value. If you give it the false value, you are creating a semaphore that will work in non-fair mode. You will get the same behavior if you don't use this parameter. If you give it the true value, you are creating a semaphore that will work in fair mode.

Controlling Concurrent access to multiple copies of a resource 
In that recipe, you implemented an example using binary semaphores. These kinds of semaphores are used to protect the access to one shared resource, or to a critical section that can only be executed by one thread at a time. But semaphores can also be used when you need to protect various copies of a resource, or when you have a critical section that can be executed by more than one thread at the same time.
In this Example, you will learn how to use a semaphore to protect more than one copy of a resource. You are going to implement an example, which has one print queue that can print documents in three different printers.

How it works...
The key of this example is in the PrintQueue class. The Semaphore object is created using 3 as the parameter of the constructor. The first three threads that call the acquire() method will get the access to the critical section of this example, while the rest will be blocked. When a thread finishes the critical section and releases the semaphore, another thread will acquire it.
In this critical section, the thread gets the index of the printer assigned to print this job. This part of the example is used to give more realism to the example, but it doesn't use any code related with semaphores.

The acquire(), acquireUninterruptibly(), tryAcquire(), and release() methods have an additional version which has an int parameter. This parameter represents the number of permits that the thread that uses them wants to acquire or release, so as to say, the number of units that this thread wants to delete or to add to the internal counter of the semaphore. In the case of the acquire(), acquireUninterruptibly(), and tryAcquire() methods, if the value of this counter is less this value, the thread will be blocked until the counter gets this value or a greater one.

If you know anyone who has started learning Java, why not help them out! Just share this post with them. Thanks for studying today!...

1 comment: