Last active
August 29, 2015 14:02
-
-
Save x746e/f4d17464882f4a1e74d9 to your computer and use it in GitHub Desktop.
Is using fair lock enough to make a fair semaphore?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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