I found simple realization of semaphore(my CustomSemaphore) and as I understand this is 'not fair' one because into secure block can enter only first thread all time(I am not sure).
How can I write fair semaphore(analog of concurrency new Semaphore(1, true);
)
public class SimpleSemaphoreSample2 {
CustomSemaphore cSem = new CustomSemaphore(1);
public static void main(String[] args) {
SimpleSemaphoreSample2 main = new SimpleSemaphoreSample2();
Semaphore sem = new Semaphore(1, true);
Thread thrdA = new Thread(main.new SyncOutput(sem, "Thread1"), "Thread1");
Thread thrdB = new Thread(main.new SyncOutput(sem, "Thread2"), "Thread2");
thrdA.start();
thrdB.start();
try {
thrdB.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("END");
}
class SyncOutput implements Runnable {
private Semaphore sem;
private String msg;
public SyncOutput(Semaphore s, String m) {
sem = s;
msg = m;
}
@Override
public void run() {
while (true) {
try {
// sem.acquire();
cSem.acquire();
System.out.println("Before");
Thread.sleep(500);
System.out.println(msg);
Thread.sleep(500);
System.out.println("After");
Thread.sleep(500);
} catch (Exception exc) {
exc.printStackTrace();
}
// sem.release();
cSem.release();
}
}
}
public class CustomSemaphore {
private int counter;
public CustomSemaphore() {
this(0);
}
public CustomSemaphore(int i) {
if (i < 0)
throw new IllegalArgumentException(i + " < 0");
counter = i;
}
public synchronized void release() {
if (counter == 0) {
this.notify();
}
counter++;
}
public synchronized void acquire() throws InterruptedException {
while (counter == 0) {
this.wait();
}
counter--;
}
}
}
enter code here
Generally, semaphores used to control resource access should be initialized as fair, to ensure that no thread is starved out from accessing a resource. When using semaphores for other kinds of synchronization control, the throughput advantages of non-fair ordering often outweigh fairness considerations.
An oxygen thread will wait for two hydrogen to come ready and then signal the oxygen count twice to let them know oxygen is ready. This is an example of a "rendezvous"—we are signaling a general semaphore to record the action of one thread and another thread can wait on it to meet up with it.
Semaphore in Java It is a type of variable which is used to manage concurrent processes and also synchronize them. It is also used to avoid race conditions. It restricts the number of threads to access a shared resource. For example, we can restrict a file to access up to 10 connections simultaneously.
In general, to use a semaphore, 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 acquires a permit, which causes the semaphore's count to be decremented. Otherwise, the thread will be blocked until a permit can be acquired.
Your semaphore is not fair because it is possible that a thread wait forever. Think about a mutex (binary semaphore) used to write a value by 3 threads. T1 acquire, T2 wait and T3 wait. Now during release, you notify and one between T2 and T3 take the semaphore (let say T2). Now T1 come back and wait. When T2 notify, T1 takes it. It can happen as many times as possible, and T3 will never have the semaphore.
One change can be to use a simple FIFO inside the semaphore. When an thread has to wait, you add his id in the Queue. Now when you notify, you notify to all the threads. The only thread which progress is the one which is at the head of the queue.
According to Java Concurrency In Practice it states that
intrinsic locking offers no deterministic fairness guarantees
Intrinsic locking here is using synchronized
. So there is no way you can make this Semaphore example fair without replacing synchronized
with Lock lock = new ReentrantLock(true);
Where the true
as constructor argument tells the ReentrantLock to be fair
Edit based on a comment by @trutheality
If you really want it to be correct without using the ReentrantLock, you can implement the Semaphore inheriting the synchronization primitives from the AbstractQueuedSynchronizer. This would prove to be quite complicated and if you can correctly write it with ReentrantLock I would suggest that. Note: ReentrantLock delegates its synchronization to the AQS.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With