Calling a lambda expression from a new thread before the main method is run causes the thread to lock up
Remi Forax
forax at univ-mlv.fr
Tue Jan 24 08:02:39 UTC 2017
----- Mail original -----
> De: "Luke Hutchison" <luke.hutch at gmail.com>
> À: "David Holmes" <david.holmes at oracle.com>
> Cc: core-libs-dev at openjdk.java.net
> Envoyé: Mardi 24 Janvier 2017 08:21:39
> Objet: Re: Calling a lambda expression from a new thread before the main method is run causes the thread to lock up
> On Mon, Jan 23, 2017 at 10:48 PM, David Holmes <david.holmes at oracle.com>
> wrote:
>
>> On 24/01/2017 2:41 PM, Luke Hutchison wrote:
>>
>>> If you run the code below, the active JVM thread (in the ExecutorService)
>>> locks up when the lambda expression is called. The Eclipse debugger is not
>>> able to stop the locked-up thread, or get a stacktrace beyond the call
>>> into
>>> the lambda.
>>>
>>
>> That looks like a variation of the classic class initialization deadlock
>> problem. Your main thread is blocked in es.submit(callable).get(); while
>> still within the static initializer of the LambdaBug class. If the executor
>> thread also needs to ensure LambdaBug is initialized (which it will in the
>> lambda case) then it will wait for the main thread to complete the
>> initialization. Hence deadlock.
>>
>
> Thanks for the explanation -- although I don't understand why a lambda
> expression requires its defining class to be initialized, whereas an
> anonymous inner class does not. The lambda body does not refer to the
> LambdaBug class. This must be due to an implicit reference to the defining
> class being added by the compiler for purposes of capturing the lexical
> scope (with this issue not getting triggered in the case of anonymous inner
> classes due to scoping differences)? However, why is the containing class
> reference even used in this case, when variables in the containing scope
> are not referenced?
The body of a lambda is desugared by the compiler into a static method, so when you submit the callable, a worker thread of the executor will try to execute the code of the static method but because the static initializer is not finished, the worker thread has to wait, then you call get() inside the static block that waits that the worker thread to finish its work => deadlock.
In case of an inner class, your code is in the inner class so the worker thread don't wait the static block to be finished, so no deadlock
As a rule of thumb, never starts a thread in a static block, you will get this classical deadlock AND your code is hard to test because static blocks tend to be executed in a random order (i.e. class loading is lazy so execution of static blocks are lazy too).
>
> If this is due to lexical scoping, shouldn't the compiler be able to add
> some runtime code to detect cases of this kind of deadlock, and throw a
> RuntimeException, or potentially even detect some of these cases statically?
>
> Why is it possible to directly call lambdas inside a static initializer
> block without causing a deadlock (in other words, why is there no deadlock
> as long as the lambdas are not executed on a different thread while the
> main thread blocks)? The calling class is still not completely initialized
> when calling lambdas directly in a static initializer block on the main
> thread.
cheers,
Rémi
More information about the core-libs-dev
mailing list