6

I am trying to find out the cause behind below mentioned code. Here if I create Thread using anonymous inner class it goes into deadlock state but with lambda expressions it works fine. I tried to find the reason behind this behavior but I could not.

public class ThreadCreationTest {

    static {
        new ThreadCreationTest();
    }

    private void call() {
        System.out.println("Hello guys!!!");
    }

    public ThreadCreationTest() {

        // when we use this thread it goes in deadlock kind of state
        Thread thread1 = new Thread(new Runnable() {
            public void run() {
                call();
            }
        });

        // This one works fine.
        Thread thread = new Thread(() -> call());

        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    public static void main(String... args) {
        System.out.println("Code finished...");
    }
}

with lambda expression output :

Hello guys!!!
Code finished...

with anonymous class :

code goes into deadlock state
M A
  • 71,713
  • 13
  • 134
  • 174
cody123
  • 2,040
  • 24
  • 29
  • Before anyone else goes down the `java.util.concurrent.Callable` rabbit hole, it still happens even if you rename `call` to `xcall` or similar. – T.J. Crowder Oct 25 '16 at 13:28
  • Really nice puzzler ;) I can give you the hint that removing the private access modificator at the call method will solve the problem but I only have a vague explanation for that. – meistermeier Oct 25 '16 at 13:45

1 Answers1

5

Decompiling with javap the inner class shows the following for the run method:

public void run();
  descriptor: ()V
  flags: ACC_PUBLIC
  Code:
    stack=1, locals=1, args_size=1
       0: aload_0
       1: getfield      #12                 // Field this$0:Ltest/ThreadCreationTest;
       4: invokestatic  #22                 // Method test/ThreadCreationTest.access$0:(Ltest/ThreadCreationTest;)V
       7: return
    LineNumberTable:
      line 31: 0
      line 32: 7
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
          0       8     0  this   Ltest/ThreadCreationTest$1;

Notice that there is a static synthetic method access$0 which in turn calls the private method call. The synthetic method is created because call is private and as far as the JVM is concerned, the inner class is just a different class (compiled as ThreadCreationTest$1), which cannot access call.

static void access$0(test.ThreadCreationTest);
  descriptor: (Ltest/ThreadCreationTest;)V
  flags: ACC_STATIC, ACC_SYNTHETIC
  Code:
    stack=1, locals=1, args_size=1
       0: aload_0
       1: invokespecial #68                 // Method call:()V
       4: return
    LineNumberTable:
      line 51: 0
    LocalVariableTable:
      Start  Length  Slot  Name   Signature

Since the synthetic method is static, it is waiting for the static initializer to finish. However, the static initializer is waiting for the thread to finish, hence causing a deadlock.

On the other hand, the lambda version does not rely on an inner class. The bytecode of the constructor relies on an invokedynamic instruction (instruction #9) using MethodHandles:

public test.ThreadCreationTest();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=3, args_size=1
         0: aload_0
         1: invokespecial #13                 // Method java/lang/Object."<init>":()V
         4: new           #14                 // class java/lang/Thread
         7: dup
         8: aload_0
         9: invokedynamic #19,  0             // InvokeDynamic #0:run:(Ltest/ThreadCreationTest;)Ljava/lang/Runnable;
        14: invokespecial #20                 // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
        17: astore_1
        18: aload_1
        19: invokevirtual #23                 // Method java/lang/Thread.start:()V
        22: aload_1
        23: invokevirtual #26                 // Method java/lang/Thread.join:()V
        26: goto          36
        29: astore_2
        30: invokestatic  #29                 // Method java/lang/Thread.currentThread:()Ljava/lang/Thread;
        33: invokevirtual #33                 // Method java/lang/Thread.interrupt:()V
        36: return
M A
  • 71,713
  • 13
  • 134
  • 174
  • The `invokedynamic` instruction is irrelevant, there will be a synthetic method `lambda$new$0` serving the same purpose as the `access$000` method for the inner class. So the only thing that matters, is, that either is `static` and the other is not. At this point, it’s important to emphasize that it is completely the compiler’s decision whether the synthetic method for a lambda expression will be `static` or not. – Holger Oct 25 '16 at 17:46
  • Indeed there is a missing snippet in the bytecode that shows the compiled lambda which I didn't include in this post (shown at the end of the javap output). I don't have access to it at the moment. – M A Oct 25 '16 at 18:50
  • The irony is, that a lambda calling a method on the class under initialization, like `() -> call()`, works, whereas a lambda not calling a method on the class, like `() -> System.out.println()`, doesn’t. Because non-capturing lambda expressions are compiled into synthetic `static` methods… – Holger Oct 26 '16 at 08:42