i found FutureTask get() method may distable LockSupport.park in oracle jdk8
my code is :
ExecutorService service = Executors.newFixedThreadPool(1, (r) -> {
Thread thread = new Thread(r);
thread.setDaemon(true);
return thread;
});
Future<String> submit = service.submit(() -> {
TimeUnit.SECONDS.sleep(5);
System.out.println("exec future task..");
return "a";
});
System.out.println(submit.get());
LockSupport.park(new Object());
System.out.println("error,unPark");
}
i thoughtSystem.out.println("error,unPark");
would not execute;but it did
exec future task..
a
error,unPark
for simulating thread schedule,I break point on FutureTask
line 418
queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q);
step over fastly before print exec future task..
after print exec future task..
for a while , continue to execute ..
then skip LockSupport.park(new Object());
and print error,unPark
i think
1.FutureTask add getting thread(main) in waiters;
2.execution thread(the thread pool) finish the task,and unpark all waiters;
3.getting thread(main) read state and found task hash finished, then return result and skip executing FutureTask locksupport.park()
4.because unpark method was executed in futuretask,then can skip LockSupport.park(new Object());
and print error,unPark
is it a bug?
The documentation does says:
Disables the current thread for thread scheduling purposes unless the permit is available.
If the permit is available then it is consumed and the call returns immediately; otherwise the current thread becomes disabled for thread scheduling purposes and lies dormant until one of three things happens:
- Some other thread invokes
unpark
with the current thread as the target; or- Some other thread interrupts the current thread; or
- The call spuriously (that is, for no reason) returns.
This method does not report which of these caused the method to return. Callers should re-check the conditions which caused the thread to park in the first place. Callers may also determine, for example, the interrupt status of the thread upon return.
The third bullet alone would be enough to tell you that you can’t assume that returning from park
implies that the condition you’re waiting for has been fulfilled.
Generally, this tool is meant for code that can not assume atomicity for the operation that checks/induces the condition and the park
/unpark
call.
As the documentation concludes you have to re-check the condition after returning from park
. Special emphasis on “re-”; since this implies that you might find out that the return from park
was not due to your condition being fulfilled, you might have consumed an unpark
that was not for you. This in turn implies, that you also must test the condition before calling park
and not call it when the condition has been fulfilled already. But if you do this, you might skip a park
when unpark
has been called, so some subsequent park
call will return immediately.
In short, even without “spurious returns”, you always have to test your specific condition before calling park
and re-check the condition after returning from park
, in a loop if you want to wait for the condition.
Note that most of it also applies to using wait
/notify
in a synchronized
block or await
/signal
when owning a Lock
. Both should be used with a pre-test loop for reliable results.