> De: "Luke Hutchison" <luke.hu...@gmail.com> > À: "Remi Forax" <fo...@univ-mlv.fr> > Cc: "David Holmes" <david.hol...@oracle.com>, core-libs-dev@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 < fo...@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