Showing posts with label semaphore. Show all posts
Showing posts with label semaphore. Show all posts

Sunday, June 19, 2011

Useful Tricks with Semaphores

Release doesn't have to be called by same thread as acquire

One interesting property of Semaphores in Java is that release doesn’t have to be called by the same thread as acquire. This means you could have a thread limiter that pools or creates threads based on a semaphore by calling acquire(). Then, the running thread could release its own semaphore permit when it completes. This is a useful property that we don’t have with normal mutexes in Java.

Dynamically increase or decrease the permits

Another trick is to increase or decrease the number of permits at runtime.
To increase the number of Permits
Contrary to what you might guess, the number of permits in a semaphore isn’t fixed, and a call to release() will always increment the number of permits, even if no corresponding acquire() call was made. Note that this can also result in bugs if you are incorrectly calling release() when no acquire() was made.

To decrease number of permits

To be specific, if 10 threads currently have permits and 7 is the new limit, we won’t try to communicate to any threads that they are now in violation of the new limit. What we can do is make sure that the next call to acquire() will block until enough threads (4, to be precise) have called release() to bring the number of outstanding permits to below the new limit. It’s not infeasible to interrupt threads that are holding permits beyond the new upper limit, but that’s beyond the scope of a simple semaphore.

If you wanted to reconfigure it to only allow 7 permits, you have several options:

  • You call acquire() three times. This might work, or it might block forever — if more than 7 permits are currently in use, one (or more) of your acquire() calls could block for an arbitrarily long amount of time. Checking availablePermits() isn’t going to help, either, since using that to gauge whether or not an acquire() call will block is a classic check-then-act race condition. Even if the race condition could be avoided, it doesn’t help: instead of blocking, you’ll just keep getting 0 back from availablePermits().
  • You could call drainPermits() repeatedly until at least 3 permits had been drained (and then release() any extra that were drained beyond the 3 you need). This could take an arbitrarily long time.
  • You could call tryAcquire() repeatedly until you’ve gotten 3 permits. This also could take an arbitrarily long time.

These approaches all share two flaws:

  1. Most importantly, they block until as many permits as you are trying to remove have become available
  2. They don’t really achieve the goal directly

To be precise, the goal is that the semaphore should release fewer permits to the threads that are using the semaphore to control access to the shared resource. Acquiring (and presumably not releasing) permits is one way to achieve the goal, but it is not necessarily the only way. Put another way, the thread that is executing the reconfiguration does not technically need to acquire permits (whether it be by acquire() or drainPermits() or tryAcquire(), etc) to fulfill this requirement, since it doesn’t need to access the shared resource that the semaphore is guarding. So we come to the last solution - Extending Semaphore to use reducePermits().

Some more useful methods

Finally, there are a few useful methods to be familiar with in Java’s Semaphore. The method acquireInterruptibly() will acquire a resource, reattempting if it is interrupted. This means no outside handling of InterruptedException. The method tryAcquire() allows us to limit how long we will wait for a permit – we can either return immediately if there is no permit to obtain, or wait a specified timeout. If you somehow have known deadlocks that you can’t fix easily or track down, you could help prevent locking up processes by using tryAcquire() with suitable timeouts.

Handling Semaphores with care

Exception is raised
As with most methods of locking or synchronization, there are some potential issues.

The number one thing to remember is, always release what you acquire. This is done by using try..finally constructs. Example:

// Bad -- semaphore not released if the exception is thrown
try {
sem.acquire();
sharedResource.doStuff();
sem.release();
} catch (IOException e) {
logger.warn("The resource is broken", e);
}

So instead use try…finally:

// Good
try {
sem.acquire();
sharedResource.doStuff();
} catch (IOException e) {
logger.warn("The resource is broken", e);
} finally {
sem.release();
}


Issue of lock ordering
The following class shows a deadlock that only the luckiest of you will avoid. You’ll notice that the two threads which acquire the two semaphore permits do so in opposite order. (try..finally is left out for the sake of brevity).

public static void main(String[] args) throws Exception {
Semaphore s1 = new Semaphore(1);
Semaphore s2 = new Semaphore(1);

Thread t = new Thread(new DoubleResourceGrabber(s1, s2));
// now reverse them ... here comes trouble!
Thread t2 = new Thread(new DoubleResourceGrabber(s2, s1));

t.start();
t2.start();

t.join();
t2.join();
System.out.println("We got lucky!");
}

private static class DoubleResourceGrabber implements Runnable {
private Semaphore first;
private Semaphore second;

public DoubleResourceGrabber(Semaphore s1, Semaphore s2) {
first = s1;
second = s2;
}

public void run() {
try {
Thread t = Thread.currentThread();

first.acquire();
System.out.println(t + " acquired " + first);

Thread.sleep(200); // demonstrate deadlock

second.acquire();
System.out.println(t + " acquired " + second);

second.release();
System.out.println(t + " released " + second);

first.release();
System.out.println(t + " released " + first);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}


If you run this, you will more than likely have a hung process. Issues of lock ordering apply to semaphores as much as regular mutexes or synchronization in Java. In some cases, timeouts (see note on tryAcquire() later in the article) can be used to prevent deadlocks from causing a process to hang up, but typically a deadlock is a symptom of a logic error which can be avoided. If you’re unfamiliar with deadlocks, I recommend you read up on them. Wikipedia has a decent article on deadlocks which applies to all languages equally.

The main things that you should be careful of when using semaphores (including binary semaphores, i.e. mutexes) are:



  • Not releasing after acquire (either missing release call or an exception is thrown and there is no finally block)
  • Long held semaphores, causing thread starvation
  • Deadlocks (as seen above)

Uses of semaphores

What are some possible uses for counting semaphores? The following come to mind:

  • Limiting concurrent access to disk (this can kill performance due to competing disk seeks)
  • Thread creation limiting
  • JDBC connection pooling / limiting
  • Network connection throttling
  • Throttling CPU or memory intensive tasks

Saturday, June 18, 2011

Java locks - Semaphores

Semaphores ensure that only n threads can enter into the critical section of the code at a given time.

Semaphores concept was invented by the famous Dutch computer scientist Edsger Dijkstra. Basically a semaphore is a counter (integer) that allows a thread to get into a critical region if the value of the counter is greater than 0. If it’s the case, the counter is decremented by one otherwise, the thread is waiting until it can go. And when the thread go away from the critical region, the counter is incremented by one to allow one more thread to pass the critical region. A semaphore is created with a certain value for its counter. So, you can execute two actions on a semaphore P and V.

V is also known as signal and it increments the semaphore. P is also known as wait and it decrements semaphore.

By example, if you have a critical that cannot be executed concurrently, you can use a semaphore :

sem mutex = new sem(1)

P(mutex)
//Critical region
V(mutex)

So you must always call by yourself the P operation before the critical region and V after it. We call a mutex (mutual exclusion) a semaphore with a value of one. So only one thread can enter the region guarded by the semaphore. This is the most used semaphore. The other use of semaphore is to guard a set of resources like database connections or a data pool. In Java, Semaphore were introduced in jdk5. A semaphore is created using the java.util.concurrent.Semaphore class. You can create easily :
Semaphore mutex = new Semaphore(1);
Semaphore available = new Semaphore(100);

The P and V operations

As discussed above, the P and V operations are represented using the acquire and release methods. The method acquire can be interrupted if the thread is interrupted. There is an ininterruptible version with the method acquireUninterruptibly(). There is also a third version with the tryAcquire method. This method acquire a permit only if there is one permit available, otherwise, this method return false directly. All the waiting methods have also an overloaded version with a timeout. You can also acquire several permits at once using the permits argument to the different versions of acquire methods.

Examples

A little example with a mutex using the same example as the previous post on Java concurrency :
public class Counter {
private int value = 0;

private final Semaphore mutex = new Semaphore(1)

public int getNextValue() throws InterruptedException {
try {
mutex.acquire();
return value++;
} finally {
mutex.release();
}
}
}
Consider the test class:
package com.vaani.semaphores.demo;

import java.util.concurrent.Semaphore;

import com.vaani.mutex.Counter;

public class MutexDemo extends Thread{
public static void main(String[] args)
{
// Limiting No on threads running to 2
Counter counter = new Counter();
for(int i=0 ;i<5;i++){
new MutexThread(String.valueOf(i),counter ).start();
}
System.out.println("End of Semaphore Test");
}
}


class MutexThread extends Thread
{

private String name = null;
private Counter counter =null;
public MutexThread(String name,Counter counter_){

this.name = name;
this.counter = counter_;
}


public void run(){
try {
System.out.println("Counter is now "+counter.getNextValue());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}

For more informations about Semaphore in Java, the best is to consult the Javadoc of the Semaphore class.

Simple Semaphore Example

Consider a simple semaphore in which we just acquire lock and our work and release it:
import java.util.*;
import java.util.concurrent.*;
class SimpleSemaphoreDemo
{
public static void main(String[] args)
{
// Limiting No on threads running to 2
Semaphore semaphore = new Semaphore(2);
for(int i=0 ;i<5;i++){
new MyThread(String.valueOf(i),semaphore).start();
}
System.out.println("End of Semaphore Test");
}
}


class MyThread extends Thread
{

private String name = null;
private Semaphore semaphore =null;
public MyThread(String name,Semaphore semaphore){

this.name = name;
this.semaphore = semaphore;
}


public void run(){
try{
semaphore.acquire();
System.out.println("Thread "+ name +" is start running");
sleep(500);
semaphore.release();
System.out.println("Thread "+ name +" Ends");
}catch(Exception exp){
exp.printStackTrace();
}
}

}
In this example, we have created 5 threads but no of running threads are limited to only 2, as the line :
Semaphore semaphore = new Semaphore(2); Then we are calling acquire and release functions. The output in this case will look like this :

Thread 0 is start running
End of Semaphore Test
Thread 1 is start running
Thread 0 Ends
Thread 2 is start running
Thread 1 Ends
Thread 3 is start running
Thread 3 Ends
Thread 2 Ends
Thread 4 is start running
Thread 4 Ends

If we put semaphore for 3 threads:
Semaphore semaphore = new Semaphore(3); Now the output will look like this :
Thread 0 is start running
Thread 1 is start running
Thread 2 is start running
End of Semaphore Test
Thread 4 is start running
Thread 2 Ends
Thread 3 is start running
Thread 0 Ends
Thread 1 Ends
Thread 3 Ends
Thread 4 Ends

Conclusion

To conclude, semaphores are a powerful ways to solve concurrency problems, but this is not adapted to all problems. If you need only mutual exclusion, synchronized blocks are a better solutions. The problems with semaphores is that you can forget to call the release method and that can cause deadlock sometimes difficult to find.


Download the source


Source code of this example can be downloaded from here.

Chitika