Calling a lambda expression from a new thread before the main method is run causes the thread to lock up
forax at univ-mlv.fr
forax at univ-mlv.fr
Tue Jan 24 08:39:30 UTC 2017
> De: "Luke Hutchison" <luke.hutch at gmail.com>
> À: "Remi Forax" <forax at univ-mlv.fr>
> Cc: "David Holmes" <david.holmes at oracle.com>, core-libs-dev at openjdk.java.net
> Envoyé: Mardi 24 Janvier 2017 09:13:17
> Objet: Re: Calling a lambda expression from a new thread before the main method
> is run causes the thread to lock up
> On Tue, Jan 24, 2017 at 12:02 AM, Remi Forax < forax at univ-mlv.fr > wrote:
>> 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
> But what is the worker thread waiting for in the case of the lambda that it is
> not waiting for in the case of the AIC? I assume this is due to the lexical
> scoping of the lambda?
it's not directly related to the lexical scoping of a lambda, it's related to the fact that the body of a lambda is not inside the class created when you transform a lambda to a class that implements the functional interface but the body of a lambda is part of the class that declares the lambda.
>> 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).
> This is the problem: I am a library author, and a user of my library ran into
> this problem. The user did not necessarily know that I was launching new
> threads in my library, and as a library author, I did not know I needed to
> advise users that they should not call my library from static blocks. It seems
> to be quite a big problem that a static block is not a standard execution
> context and can lead to deadlocks.
Users should always know when they starts a thread, otherwise they may send you objects that are not thread safe, multi-threading is not something you can hide to your users.
Concurrency is already hard, hidden concurrency is harder :)
static blocks are executed with a kind of class lock to prevent several threads to initialize the same class, so like any synchronized-like block, they should be as small as possible.
> Is there any way I can detect this in my code? I guess I could just get a
> stacktrace, and look for "<init>" or similar somewhere in the stacktrace, then
> throw an exception if called in this context? Is this a reliable enough test
> for being run in a static initializer block?
The real name of a static block is <clinit>, not <init> which is used for constructor.
If your API clearly states that you will start a thread, the burden to fix deadlocks is transferred to your users :)
The extreme of that is that some people use the javadoc to cover their own ass, "yes, i know this behavior is stupid but look, it's written in the javadoc".
Here is your code slightly modified, with no lambda, that deadlock too:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class NoLambdaBug {
static {
startUp();
}
static void iamstatic() {
System.out.println("Inner class method executed");
}
private static void startUp() {
System.out.println("Entering startUp()");
ExecutorService es = Executors.newSingleThreadExecutor();
try {
Callable<Void> callable = new Callable<Void>() {
@Override
public Void call() throws Exception {
iamstatic();
return null;
}
};
es.submit(callable).get();
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
} finally {
es.shutdown();
}
System.out.println("Exiting startUp()");
}
public static void main(String[] args) {
System.out.println("Exiting main");
}
}
Rémi
More information about the core-libs-dev
mailing list