Reading java language specification - memory model I cam across this statement .
A thread t may lock a particular monitor multiple times; each unlock
reverses the effect of one lock operation.
Then I went to try this by using the code below, however it doesn't seem to work.
Either I don't understand the statement or my code is not doing what I want it to ...
public class MultipleLocks {
private static final Object LOCK = new Object();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
synchronized (LOCK) {
//some code ...
}
}
synchronized (LOCK) {
try {
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
Thread t2 = new Thread(() -> {
synchronized (LOCK) {
System.out.println("I can acquire a lock !");
}
});
t2.start();
synchronized (LOCK) {
LOCK.notify();
}
}
}
11 - Locks
Unlock only ONCE ?
The code show that only one lock operation is required, or I am missing something ?
EDIT:
If my code is wrong, how can I simulate acquiring a lock n-times and release it n-time, and if n-1 times is released then the the thread should keep locking the lock ?
解决方案
Your question "The code show that only one lock operation is required, or I am missing something ?" has been answered by @Pete Kirkham.
The answer to the main question "How many unlock operation are required to unlock an object that is locked several times in Java?" is the one you quoted.
The following code shows how Thread t1 "lock a particular monitor multiple times" and that "each unlock reverses the effect of one lock operation".
Thread t1 enters the synchronize block three times. Evertime a synchronize block is entered an (additional) lock is aquired. Everytime a synchronize block is left a lock was released. Therefore first after t1 left allsynchronize blocksThread t2 can enter the synchronize block.
public class Locking {
private static Object o = new Object();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(Locking::lockSeveralTimes);
Thread t2 = new Thread(Locking::lockOnce);
t1.start();
Thread.sleep(100); // give t1 some time to start
t2.start();
}
protected static void lockOnce() {
synchronized (o) {
System.out.println("DONE");
}
}
protected static void lockSeveralTimes() {
try {
System.out.println("Has Lock: " + Thread.holdsLock(o));
synchronized (o) {
System.out.println("Aquired Lock: " + Thread.holdsLock(o) + " / Times: " + getLockedMonitorsCount());
waitOneSecond();
synchronized (o) {
System.out.println("Aquired Lock: " + Thread.holdsLock(o) + " / Times: " + getLockedMonitorsCount());
waitOneSecond();
synchronized (o) {
System.out.println("Aquired Lock: " + Thread.holdsLock(o) + " / Times: " + getLockedMonitorsCount());
waitOneSecond();
System.out.println("Going to release lock. Still holds: " + Thread.holdsLock(o) + " / Times: " + getLockedMonitorsCount());
}
waitOneSecond();
System.out.println("Going to release lock. Still holds: " + Thread.holdsLock(o) + " / Times: " + getLockedMonitorsCount());
}
waitOneSecond();
System.out.println("Going to release lock. Still holds: " + Thread.holdsLock(o) + " / Times: " + getLockedMonitorsCount());
}
System.out.println("Still holds: " + Thread.holdsLock(o));
} catch (Exception e) {
e.printStackTrace();
}
}
protected static void waitOneSecond() throws InterruptedException {
Thread.sleep(1000);
}
protected static int getLockedMonitorsCount() {
return ManagementFactory.getThreadMXBean().getThreadInfo(new long[] { Thread.currentThread().getId() }, true, false)[0].getLockedMonitors().length;
}
}
The output is:
Has Lock: false
Aquired Lock: true / Times: 1
Aquired Lock: true / Times: 2
Aquired Lock: true / Times: 3
Going to release lock. Still holds: true / Times: 3
Going to release lock. Still holds: true / Times: 2
Going to release lock. Still holds: true / Times: 1
Still holds: false
DONE
Runs for me with JDK 8.