2015-11-12 61 views
6

私はJ.Brochの効果的なJavaを読んでいますが、今は私はエイリアンメソッドについてのセクションにあります。Java並行処理におけるエイリアンメソッドの理解

私は、Javaの並行性とそれらが行う可能性のある害から、エイリアンメソッドを理解しようとしています。彼が言ったように、私たちは基本的に、エイリアンメソッドが何をすることができるのか分からず、デッドロックを起こすことがあります。私は、このようなデッドロック動作は、以下のシンプルなアプリを書いて再現してみました(外国人の方法は、単純化のために同じクラスである):

public class App { 
    private static StringBuffer lines = new StringBuffer(); 

    public static void modifyLines(){ 
     System.out.println("Invocation modifyLines() started by " + Thread.currentThread().getName()); 
     synchronized (lines) { 
      System.out.println("Entering modifyLines() synchronized " + Thread.currentThread().getName()); 
      lines.append("Modified"); 
     } 
    } 

    public static void main(String[] args) throws InterruptedException { 
     synchronized (lines) { 
      System.out.println("Entering main() synchronized by " + Thread.currentThread().getName()); 
      alienMethod(); 
     } 
    } 

    public static void alienMethod(){ 
     ExecutorService es = Executors.newSingleThreadExecutor(); 
     es.submit(new Runnable() { 
      @Override 
      public void run() { 
       modifyLines(); 
      } 
     }); 
     es.shutdown(); 
    } 
} 

私は場所を取るために、デッドロックを期待していることalienMethod()に呼び出すことによって生成されたスレッドだろうmodifyLines()内の同期ブロックには決して入力しないでください。しかし、プログラムは以下を出力します。

Entering main() synchronized by main 
Invocation modifyLines() started by pool-1-thread-1 
Entering modifyLines() synchronized pool-1-thread-1 

これは、デッドロックが発生しなかったことを意味します。どうして?エイリアンメソッドの例で何が問題になっていますか?

+2

効果的なJavaの第2版では、並行性に関する話題のアイテム。 Brian Goetzによる「Java Concurrency in Practice」で詳しく説明されています。 – scottb

答えて

3

alienMethod()は、送信されたタスクが完了するまで待機しません。あなたはそれを待つ場合は、デッドロックがあります:

public static void alienMethod() throws InterruptedException{ 
    ExecutorService es = Executors.newSingleThreadExecutor(); 
    es.submit(new Runnable() { 
     @Override 
     public void run() { 
      modifyLines(); 
     } 
    }); 
    es.shutdown(); 
    es.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS); 
} 
+0

実際、そうです。これを試して、デッドロックを起こした。 –

3

をこれは非常に古い質問で、答えも受け入れられているが、私は答えは全く正しくないと思うので、私はこの古い墓を掘るための時間がかかったしなければなりませんでした誤解を招く恐れがあります。

まず最初に、受け入れられた答えがどのように壊れているかを強調してみましょう。es.awaitTermination(2, TimeUnit.SECONDS);で実行すると、デッドロックは発生しません。デッドロックは、2つのスレッドが互いにロックを解放するのを待っているときに発生します。通常、少なくとも2つのロックと2つのスレッドを持つデッドロックが発生します。提案された答えによると、メインスレッドがawaitTerminationを使用して保持され、メインスレッドがロックを保持しているので、メインスレッドがロックを解放するまで新しいスレッドが "待機"しなければならなかったので、今度はLong.MAX_VALUEでこの待機期間巨大なので、デッドロックのように見えますが、のように見えますが、実際にはデッドロックの代わりに「待機」していました。デッドロックを解決するには、デッドロックと待機の違いがあります。

現在、エイリアンメソッドに来ています:基本的にクラスのメソッドは、それについての情報、つまりメソッドの実装の詳細がない場合は、 "エイリアンメソッド"とみなされます。クラスが他のクラスの実装についての情報を持っていない(これはまた、 "loose coupling")ので、クラスBのすべてのメソッドはクラスAの "エイリアン"ですが、考慮しません外部の一般的なコンテキストでは、それが期待されているので、私たちは同期のコンテキストでのみ、外国人のメソッドとしてメソッドを呼び出す、したがって同期があると、同期ブロックからオブジェクトが何も情報デッドロックが発生する可能性があるかどうかをオブジェクトが確認できない場合、そのメソッドは "alientメソッド"と呼ばれます。

以下は、クライアントメソッドが何をしているのか、デッドロックが発生する可能性があるか、またはデッドロックが発生する可能性があるかどうかを示します(* "*****" *)デッドロックが発生しない可能性があります。 ここではデッドロックがあり、待ちません、私は待っているコードを使用していません。

SetObserver。Javaの:

public interface SetObserver { 
    void added(MyClass mc, SetObserver sc); 
} 

MyClass.java:

import java.util.concurrent.*; 

public class MyClass { 

    static Object o1 = new Object(); 

    public void test1(){ 
     synchronized(o1){ 
      System.out.println("test1"); 
     } 
    } 

    public void test3(SetObserver sc) throws InterruptedException{ 
     synchronized(o1){ 
      for (int i = 0; i < 100; i++) { 
       System.out.print("test3 >>" + i); 
       sc.added(this, sc); 
       synchronized(sc){ 
        System.out.println("<<"); 
       } 
      } 
     } 
    } 

    public static void main(String[] args) throws InterruptedException { 
     MyClass mc = new MyClass(); 
     mc.test3(new SetObserver() { 

      @Override 
      public void added(final MyClass mc, final SetObserver sc) { 
       // ***** This will not cause deadlock because it doesn't spawn a new thread, even though it synchronize on same object. ***** 
       /*synchronized(sc){ 
        mc.test1(); 
       }*/ 

       // ***** This causes a deadlock because it spawns a new thread, so it will cause lock contention among threads. ***** 
       ExecutorService xc = Executors.newFixedThreadPool(1); 
       xc.execute(new Runnable() { 

        @Override 
        public void run() { 
         synchronized(sc){ 
          System.out.println("Calling test1"); 
          mc.test1(); 
         } 
        } 
       }); 
       xc.shutdown(); 
      } 
     }); 
    } 
} 
+2

非常に便利な答えです!ありがとうございました。 – zhfkt

+0

@zhfktご連絡いただきありがとうございます、ご意見ありがとうございます。 – hagrawal