【问题标题】:Using anonymous runnable class code goes in deadlock state but with lambda it works fine使用匿名可运行类代码进入死锁状态,但使用 lambda 它工作正常
【发布时间】:2016-10-25 13:18:19
【问题描述】:

我正在尝试找出下面提到的代码背后的原因。在这里,如果我使用匿名内部类创建线程,它会进入死锁状态,但使用 lambda 表达式它可以正常工作。我试图找出这种行为背后的原因,但我找不到。

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...");
    }
}

带有 lambda 表达式输出:

Hello guys!!!
Code finished...

使用匿名类:

code goes into deadlock state

【问题讨论】:

  • 在其他人进入 java.util.concurrent.Callable 兔子洞之前,即使您将 call 重命名为 xcall 或类似名称,它仍然会发生。
  • 非常好的谜题;)我可以给你提示,在调用方法中删除私有访问修饰符将解决问题,但我对此只有一个模糊的解释。

标签: java multithreading lambda java-8 anonymous-class


【解决方案1】:

使用javap 反编译内部类会为run 方法显示以下内容:

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;

请注意,有一个静态合成方法access$0,它又调用私有方法call。创建合成方法是因为call是私有的,而对于JVM而言,内部类只是一个不同的类(编译为ThreadCreationTest$1),无法访问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

由于合成方法是静态的,它正在等待静态初始化程序完成。但是,静态初始化程序正在等待线程完成,从而导致死锁。

另一方面,lambda 版本不依赖于内部类。构造函数的字节码依赖于使用MethodHandlesinvokedynamic 指令(指令#9):

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

【讨论】:

  • invokedynamic 指令无关紧要,将有一个合成方法lambda$new$0 与内部类的access$000 方法具有相同的目的。所以唯一重要的是,一个是static,另一个不是。在这一点上,需要强调的是,lambda 表达式的合成方法是否为static 完全由编译器决定。
  • 确实,字节码中缺少一个 sn-p,它显示了我在这篇文章中没有包含的已编译 lambda(显示在 javap 输出的末尾)。我目前无法访问它。
  • 具有讽刺意味的是,在初始化的类上调用方法的 lambda,如 () -&gt; call(),可以工作,而没有在类上调用方法的 lambda,如 () -&gt; System.out.println(),则不起作用.因为非捕获 lambda 表达式被编译成合成的static 方法……
猜你喜欢
  • 2018-12-08
  • 1970-01-01
  • 2019-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多