Skip to content

Instantly share code, notes, and snippets.

@x746e
Last active August 29, 2015 14:02
Show Gist options
  • Save x746e/f4d17464882f4a1e74d9 to your computer and use it in GitHub Desktop.
Save x746e/f4d17464882f4a1e74d9 to your computer and use it in GitHub Desktop.
Is using fair lock enough to make a fair semaphore?
tn@tns-laptop ~/mfiles/learn/coursera/posa-002
% java Test
Thread-B: about to acquire the lock at the start of acquire()
Thread-B: got the lock at the start of acquire()
Thread-B: got the permit
Thread-B: releasing the lock at the end of acquire()
>> Locking lockB in threadB run()
>> Putting threadB to sleep
Thread-A: about to acquire the lock at the start of acquire()
Thread-A: got the lock at the start of acquire()
Thread-A: no available permits, going to wait
>> Locking lockB in main()
>> Waking threadB
>> threadB waked up
Thread-B: about to acquire the lock at the start of release()
Thread-C: about to acquire the lock at the start of acquire()
Thread-B: got the lock at the start of release()
Thread-B: signalled waiting threads
Thread-B: releasing the lock at the end of release();
Thread-C: got the lock at the start of acquire()
Thread-C: got the permit
Thread-C: releasing the lock at the end of acquire()
>> Thread-C got the resource!
Thread-A: waked up from the await() call
Thread-A: no available permits, going to wait
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;
public class SimpleSemaphore {
private final ReentrantLock lock;
private final Condition permitsAvailable;
private int permits;
public SimpleSemaphore(int permits, boolean fair) {
this.lock = new ReentrantLock(fair);
this.permitsAvailable = lock.newCondition();
this.permits = permits;
}
public void acquireUninterruptibly() {
log("about to acquire the lock at the start of acquire()");
lock.lock();
log("got the lock at the start of acquire()");
try {
while (permits == 0) {
log("no available permits, going to wait");
permitsAvailable.awaitUninterruptibly();
log("waked up from the await() call");
}
permits -= 1;
log("got the permit");
} finally {
log("releasing the lock at the end of acquire()");
lock.unlock();
}
}
void release() {
log("about to acquire the lock at the start of release()");
lock.lock();
try {
Thread.sleep(100);
} catch (InterruptedException exc) {}
log("got the lock at the start of release()");
try {
permits += 1;
permitsAvailable.signal();
log("signalled waiting threads");
} finally {
lock.unlock();
log("releasing the lock at the end of release();");
}
}
private void log(String message) {
System.out.println(Thread.currentThread().getName() +
": " + message);
}
}
/*
* 1. Thread-A is blocked in waiting for the condition in `Semaphore.acquire`.
* 2. Then thread-B gets the lock in `Semaphore.release` method.
* 3. Then thread-C becomes blocked on `lock.lock()` in `Semaphore.acquire()`.
* 4. And then thread-B signals on the condition.
* 5. Thread-A is waked up from waiting on the condition, and tries to
* reacquire the lock. Not it's second thread blocked trying to acquire the
* lock.
* 6. Thread-B releases the lock.
* 7. Thread-C is the first in waiting queue of the lock, so it gets the lock
* and the shared resource.
*
* So, despite thread-A called semaphore's `acquire` method before thread-C,
* thread-C got the resource before thread-A. That doesn't seem fair.
*/
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;
class Test {
public static void main(String[] args) throws InterruptedException {
final SimpleSemaphore sema = new SimpleSemaphore(1, true);
final ReentrantLock lockB = new ReentrantLock();
final Condition conditionB = lockB.newCondition();
Thread threadA = new Thread() {
public void run() {
sema.acquireUninterruptibly();
System.out.println(">> Thread-A got the resource!");
}
};
threadA.setName("Thread-A");
Thread threadB = new Thread() {
public void run() {
sema.acquireUninterruptibly();
System.out.println(">> Locking lockB in threadB run()");
lockB.lock();
System.out.println(">> Putting threadB to sleep");
conditionB.awaitUninterruptibly();
System.out.println(">> threadB waked up");
lockB.unlock();
sema.release();
}
};
threadB.setName("Thread-B");
Thread threadC = new Thread() {
public void run() {
sema.acquireUninterruptibly();
System.out.println(">> Thread-C got the resource!");
}
};
threadC.setName("Thread-C");
threadB.start();
Thread.sleep(100);
threadA.start();
Thread.sleep(100);
System.out.println(">> Locking lockB in main()");
lockB.lock();
System.out.println(">> Waking threadB");
conditionB.signal();
lockB.unlock();
threadC.start();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment