2016-10-25 6 views
6

私は以下のコードの原因を調べようとしています。ここで、匿名の内部クラスを使用してスレッドを作成すると、デッドロック状態になりますが、ラムダ式では正常に動作します。私はこの行動の背後にある理由を見つけようとしましたが、できませんでした。匿名クラスと匿名の実行可能クラスコードを使用すると、デッドロック状態になりますが、ラムダでうまく動作します。

Hello guys!!! 
Code finished... 

:ラムダ式の出力と

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

code goes into deadlock state 
+0

を誰にも 'java.util.concurrent.Callable'のウサギの穴を下る前に名前を変更しても、それはまだ'起こります'' xcall'またはそれに類するものを呼び出します。 –

+0

本当に素敵なパズル;)コールメソッドでプライベートアクセスモディファイアを削除すると問題は解決しますが、そのための曖昧な説明しかありません。 – meistermeier

答えて

5

逆コンパイルjavap内部クラスでrun方法については、以下を示す:

今度はプライベートメソッドを呼び出します call静的合成法 access$0があること
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; 

注意してください。 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 

合成メソッドは静的なので、静的な初期化子が終了するのを待機しています。ただし、静的イニシャライザはスレッドが終了するのを待っているため、デッドロックが発生します。

一方、ラムダバージョンは内部クラスに依存しません。コンストラクタのバイトコードはinvokedynamic命令に依存している(命令#9)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 
+0

'invokedynamic'命令は無関係です。合成メソッド' lambda $ new $ 0'は、内部クラスの 'access $ 000'メソッドと同じ目的を果たします。重要なのは唯一のものです。つまり、どちらかが静的であり、他方はそうではありません。この時点で、ラムダ式の合成メソッドが静的であるかどうかはコンパイラの判断であることを強調することが重要です。 – Holger

+0

確かに、このポストには含まれていないコンパイル済みのラムダを示すバイトコードに抜け落ちがあります(javap出力の最後に表示されています)。私は現時点でそれにアクセスできない。 – manouti

+0

アイロニーは、 '() - > call()'のように初期化されているクラスのメソッドを呼び出すラムダが動作するのに対し、ラムダは '() - > System.out .println() 'は、そうではありません。非捕捉ラムダ式は合成 'static'メソッドにコンパイルされるので... – Holger

関連する問題