// Not really how java.util.concurrent.Semaphore is implemented
@ThreadSafe
public class SemaphoreOnLock {
private final Lock lock = new ReentrantLock();
// CONDITION PREDICATE: permitsAvailable (permits > 0)
private final Condition permitsAvailable = lock.newCondition();
@GuardedBy("lock") private int permits;
SemaphoreOnLock(int initialPermits) {
lock.lock();
try {
permits = initialPermits;
} finally {
lock.unlock();
}
}
/* other code omitted.... */
I have a question about the sample above which is extracted from Java Concurrency in Practice Listing 14.12 Counting Semaphore Implemented Using Lock.
I am wondering why we need to acquire the lock in the constructor (as shown lock.lock() is invoked).
As far as i know, constructor is atomic (except the reference escaped) as no other thread can get the reference, hence, the half-constructed-object is not visible to other threads.
Therefore, we do not need the synchronized modifier for constructors.
Besides, we do not need to worry about the memory visibility as well, as long as the object is safely published.
So, why do we need to get the ReentrantLock object inside the constructor?
It is not true. The object is visible to other threads at the time of construction if it has any non final/volatile fields. Therefore, other threads might see a default value for
permitsi.e0which might not be consistent with the current thread.The Java memory model offers a special guarantee of initialization safety for immutable objects (object with only final fields). An object reference visible to another thread does not necessarily mean that the state of that object is visible to the consuming thread –
JCP $3.5.2From Listing 3.15 of Java Concurrency in Practice: