Semaphore
Semaphore acts as a permit. The thread that wants access to the shared resource tries to acquire a permit. If the semaphore's count is greater than zero, then the thread is able to get a permit. Once a thread gets the permit, the semaphore's count decrements. Otherwise, the thread will be blocked until a permit can be acquired.
When the thread no longer needs access to the shared resource, it releases the permit. Repleasing the permit causes the semaphore's count to be incremented.
Java's Semaphore class implements this mechanism.
Semaphore has the two constructors shown here:
Semaphore(int num)
Semaphore(int num, boolean how)
num specifies the initial permit count. num specifies the number of threads that can access a shared resource at any one time. If num is one, then only one thread can accessthe resource at any one time. By default, waiting threads are granted a permit in an undefined order. By setting how to true, you can ensure that waiting threads are granted a permit in the order in which they requested access.
To acquire a permit, call the acquire( ) method, which has these two forms:
void acquire( ) throws InterruptedException
- acquires one permit.
void acquire(int num) throws InterruptedException
- acquires num permits.
If the permit cannot be granted at the time of the call, then the invoking thread suspends until the permit is available.
To release a permit, call release( ), which has these two forms:
void release( )
- releases one permit.
void release(int num)
- releases the number of permits specified by num.
import java.util.concurrent.Semaphore;
public class Main {
public static void main(String args[]) {
Semaphore sem = new Semaphore(1);
new Producer(sem, "A");
new Consumer(sem, "B");
}
}
class Shared {
static int count = 0;
}
class Producer implements Runnable {
String name;
Semaphore sem;
Producer(Semaphore s, String n) {
sem = s;
name = n;
new Thread(this).start();
}
public void run() {
try {
sem.acquire();
for (int i = 0; i < 5; i++) {
System.out.println(name + ": " + Shared.count++);
Thread.sleep(1000);
}
} catch (InterruptedException exc) {
System.out.println(exc);
}
sem.release();
}
}
class Consumer implements Runnable {
String name;
Semaphore sem;
Consumer(Semaphore s, String n) {
sem = s;
name = n;
new Thread(this).start();
}
public void run() {
try {
sem.acquire();
for (int i = 0; i < 5; i++) {
System.out.println(name + ": " + Shared.count--);
Thread.sleep(1000);
}
} catch (InterruptedException exc) {
System.out.println(exc);
}
sem.release();
}
}
An implementation of a producer and consumer that use semaphores to control synchronization.
import java.util.concurrent.Semaphore;
public class Main {
public static void main(String args[]) {
Queue queue = new Queue();
new Consumer(queue);
new Producer(queue);
}
}
class Producer extends Worker {
Producer(Queue q) {
super(q);
}
public void run() {
for (int i = 0; i < 20; i++)
queue.produce(i);
}
}
class Consumer extends Worker{
Consumer(Queue q) {
super(q);
}
public void run() {
for (int i = 0; i < 20; i++)
queue.consume();
}
}
abstract class Worker implements Runnable {
Queue queue;
Worker(Queue q) {
this.queue = q;
new Thread(this, "Consumer").start();
}
}
class Queue {
int currentValue;
// Start with consumer semaphore unavailable.
static Semaphore semaphoreConsumer = new Semaphore(0);
static Semaphore semaphoreProducer = new Semaphore(1);
void consume() {
try {
semaphoreConsumer.acquire();
} catch (Exception e) {
System.out.println("Exception caught");
}
System.out.println("consume: " + currentValue);
semaphoreProducer.release();
}
void produce(int n) {
try {
semaphoreProducer.acquire();
} catch (Exception e) {
System.out.println("Exception caught");
}
this.currentValue = n;
System.out.println("produce: " + n);
semaphoreConsumer.release();
}
}