As shown in the code, locksupport.park () is called twice in thread t1. After LockSupport.park () for the first time, the main Thread wakes up Thread t1 with t1.interrupt() and then calls thread.interrupted () in t1 to clear the interrupted status of the thread. But Locksupport.park () does not suspend the thread a second time. Oddly enough, After comment out the [log.debug("test.......")], locksupport.park () can pause the thread a second time. What is the reason for this?
@Slf4j
public class Test08Park4 {
public static void main(String[] args) throws InterruptedException{
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
LockSupport.park();
/**
* With this log print, locksupport.park () cannot stop threads;
* If you comment this out, locksupport.park () can stop the thread
*/
log.debug("test.......");
Thread.interrupted();
LockSupport.park();
log.debug("finished...");
}
});
t1.start();
sleep(1000);
t1.interrupt();
}
}
if do not comment [log.debug("test.......")] out,the result:
22:37:05.435 [Thread-0] DEBUG test.Test08Park4 - test.......
22:37:05.439 [Thread-0] DEBUG test.Test08Park4 - finished...
if comment [log.debug("test.......")] out,the result:
//nothing
CodePudding user response:
Reading docs is fundamental.
As the docs indicate, LockSupport.park()
is allowed to spuriously return. In other words, the docs literally spell out:
- Q: "Why did the park() call return so quickly?"
- A: Because the JVM felt like it.
LockSupport's locks are thread-global. There is exactly one semaphore (which LockSupport calls a 'permit') for each thread, you can't make more and you can't make less. Basically meaning, exactly one system within your JVM can use it, because if two use it, they confuse each other.
As the docs also say:
Basic thread blocking primitives for creating locks and other synchronization classes.
Which in essence means: Why are you doing this? If you want locking behaviour, use e.g. ReentrantLock
from the java.util.concurrent
package; this isn't meant for you, and hence it has all sorts of bizarre weirdness in it, such as park()
's rule that it is allowed spurious returns.
Most likely the log.debug
code either [A] ends up calling LockSupport.unpark(ownThread)
for some reason, which means the next park()
call returns immediately, or [B] it's a timing matter. log.debug
is not 'free', many log frameworks run 'in-place', meaning, the log.debug
call actually goes out on disk and fsyncs, meaning, it takes a very long time indeed compared to your average non-disk-interacting java instruction (hundreds of instructions worth of time). That time is enough for the scheduler to fly by it, especially considering 'write to disk' or 'fsync' is a natural stopping point (threads in java are pre-emptive, but if you give the scheduler an excuse to pause your thread, it will usually take it).
In the end, the docs are clear: You don't get an answer to your 'why' question. The specs give the JVM free reign not to have to explain to you why park()
spuriously returns. Therefore there isn't much point chasing down the why for this case - even if you have an answer, tomorrow there can be a different reason. If your code can't handle spurious returns on LockSupport.park()
, your code is by definition broken. Even if you can make it work on your machine, today, with this phase of the moon, then that's no guarantee it would work fine tomorrow.
Once your code can handle spurious returns, figuring out why it spuriously returns here isn't interesting anymore. Thus, solution: Deal with spurious returns properly. Or, more likely, don't use this for locks, use something more friendly to end users.