2016-08-28 7 views
1

私はExecutorServiceを使用して2つの独立したスレッドを作成しました。今私はちょうど1つのスレッドがファイルにデータを書き込んで、別のスレッドがファイルにデータを書き込んでいるスレッドからの通知を受け取った後にそれを読むことを望みますが、出力には何も表示されません。2つの独立したスレッドで待機して通知する方法はありますか?

My code is: 
package threadingexamples; 

import java.io.BufferedReader; 
import java.io.BufferedWriter; 
import java.io.File; 
import java.io.FileReader; 
import java.io.FileWriter; 
import java.util.concurrent.Callable; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.TimeUnit; 

public class ThreadingExamples { 

public static void main(String[] args) throws InterruptedException { 
    ExecutorService es = Executors.newFixedThreadPool(2); 
    es.submit(new ForLoo1()); 
    es.submit(new ForLoop2()); 

    es.shutdown(); 
    es.awaitTermination(1, TimeUnit.DAYS); 

    System.exit(0); 
} 

} 

class ForLoo1 implements Callable<Object> { 

@Override 
public Object call() throws Exception { 

    System.out.println("I am writing content into file...."); 

    String s = "This is the content to write into a file"; 

    File file = new File("/home/f.txt"); 

    if (!file.exists()) { 
     file.createNewFile(); 
    } 

    FileWriter fw = new FileWriter(file); 
    BufferedWriter bw = new BufferedWriter(fw); 
    bw.write(s); 
    bw.close(); 
    System.out.println("Now you can read content from files..."); 
    notify(); 
    return null; 
} 

} 

class ForLoop2 implements Callable<Object> { 

    @Override 
    public Object call() throws Exception { 

     wait(); 
     System.out.println("Okay i am now going to read content of files..."); 

     BufferedReader br = new BufferedReader(new FileReader("f.txt")); 
     String str; 
     while ((str = br.readLine()) != null) { 
      str = str + ""; 
     } 
     System.out.println("I am done with reading....."); 
     System.out.println(str); 
     return null; 
    } 

} 
+4

スレッド1はForLoop1インスタンスでnotify()を呼び出し、スレッド2はForLoop2インスタンスでwait()を呼び出します。それはうまくいかない。さらに、あなたは待機中にwhileループを使用していません。低レベルの待機と通知を使用しないでください。 CountDownLatchのように、より高いレベルで使いやすい抽象化を使用します。 –

+1

スレッド間に同期がありません。待機するものがあることを確認するまで、「待機」を呼び出すことはできません。 'call'が完了すると、既に起こったことを待っています。これは永遠に待っていることを意味します。 –

+1

[Semaphore](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Semaphore.html)または[CountDownLatch](https://docs.oracle.com)をご覧ください。 –

答えて

-1

EDIT:これは何もしない例です。 CountDownLatchメソッドを使用してください。ほとんどのプログラムでnotifyまたはwaitを使用する必要はありません。また、以下のコメントを読んで、なぜこれが悪い考えであるかを正確に見てください。追加された​​ブロック(notifywaitを呼び出すために必要です)があっても、デッドロックにつながる可能性のある競合状態がまだあります。ForLoop2


あなたはほぼ手に入れました。上記のコードでwait()notify()を呼び出すと、別のオブジェクトで呼び出されています。これを試してください:

Object monitorObject = new Object(); 
es.submit(new ForLoo1(monitorObject)); 
es.submit(new ForLoop2(monitorObject)); 

... 


class ForLoop1 implements Callable<Object> { 
    private final Object monitorObject; 

    public ForLoop1(Object monitorObject) { 
     this.monitorObject = monitorObject; 
    } 

    @Override 
    public Object call() throws Exception { 

     ... 
     synchronized(monitorObject) { 
      monitorObject.notify(); 
     } 
    } 
} 

class ForLoop2 implements Callable<Object> { 
    private final Object monitorObject; 

    public ForLoop2(Object monitorObject) { 
     this.monitorObject = monitorObject; 
    } 

    @Override 
    public Object call() throws Exception { 

     synchronized(monitorObject) { 
      monitorObject.wait(); 
     } 
     ... 
    } 
} 
+0

これは優れていますが、依然として間違っています(偽の起床の可能性があります)。 waitは常に条件を待つループで呼び出されるべきです。 –

+1

また、waitを呼び出して通知するには、monitorObjectのロックを保持する必要があります。 –

+1

最も重要なことは、 'ForLoop1'オブジェクトが' monitorObject.notify() '呼び出しを既に超えている場合、' ForLoop2'オブジェクトが 'monitorObject.wait()'を呼び出さないことを保証する必要があります。 OPのmain()ルーチンのコードは、 'ForLoop1'タスクが常に最初に実行されることを保証するものではありません。 –

2

以下のコードで目的の効果を達成できます。 をForLoop2に呼び出すと、countDownが呼び出された後にスレッドが起動するのは、ForLoop1です。 CountDownLatchは、多彩な同期ツールです。

import java.io.BufferedReader; 
import java.io.BufferedWriter; 
import java.io.File; 
import java.io.FileReader; 
import java.io.FileWriter; 
import java.util.concurrent.Callable; 
import java.util.concurrent.CountDownLatch; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.TimeUnit; 

public class ThreadingExamples { 

public static void main(String[] args) throws InterruptedException { 
    final CountDownLatch cdl = new CountDownLatch(1); 
    ExecutorService es = Executors.newFixedThreadPool(2); 
    es.submit(new ForLoo1(cdl)); 
    es.submit(new ForLoop2(cdl)); 
    es.shutdown(); 
    es.awaitTermination(1, TimeUnit.DAYS); 

} 

} 

class ForLoo1 implements Callable<Object> { 
    CountDownLatch cdl; 
    public ForLoo1(CountDownLatch cdl){ 
     this.cdl=cdl; 
    } 

@Override 
public Object call() throws Exception { 

    System.out.println("I am writing content into file...."); 

    String s = "This is the content to write into a file"; 

    File file = new File("/home/f.txt"); 

    if (!file.exists()) { 
     file.createNewFile(); 
    } 

    FileWriter fw = new FileWriter(file); 
    BufferedWriter bw = new BufferedWriter(fw); 
    bw.write(s); 
    bw.close(); 
    System.out.println("Now you can read content from files..."); 
    cdl.countDown(); 
    return null; 
} 

} 

class ForLoop2 implements Callable<Object> { 
    CountDownLatch cdl; 
    public ForLoop2(CountDownLatch cdl){ 
     this.cdl=cdl; 
    } 

    @Override 
    public Object call() throws Exception { 

     cdl.await(); 
     System.out.println("Okay i am now going to read content of files..."); 

     BufferedReader br = new BufferedReader(new FileReader(new File("/home/f.txt"))); 
     String str; 
     System.out.println("I am done with reading....."); 
     while ((str = br.readLine()) != null) { 
      System.out.println(str); 
     } 

     return null; 
    } 

} 
+0

これは、コードに少し説明を加えて、OPの問題が何であったのか、それらを解決するためにどのような変更が加えられたのかを指摘した場合に、より役に立ちます。 –

+1

CountDownLatchは汎用ツールです。ForLoo1が終了した後にForLoop2のスレッドを実行しています – zpc

+0

あなたの投稿自体に上記の情報を追加してください。 –

0

これは、将来を使用して行うことができます。最初のタスクを送信すると、タスクが完了すると、2番目のタスクが最初のタスクの戻り値を取得するために使用できるFutureが返されます。 futureを受け取るCallableタスクは、getメソッドが結果を受け取るまでブロックします。

package threadingexamples; 

import java.io.BufferedReader; 
import java.io.BufferedWriter; 
import java.io.File; 
import java.io.FileReader; 
import java.io.FileWriter; 
import java.util.concurrent.Callable; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.TimeUnit; 
import java.util.concurrent.Future; 

public class ThreadingExamples { 

    public static void main(String[] args) throws InterruptedException { 

     ExecutorService es = Executors.newFixedThreadPool(2); 
     Future<Object> future = es.submit(new ForLoo1()); 
     es.submit(new ForLoop2(future)); 

     es.shutdown(); 
     es.awaitTermination(1, TimeUnit.DAYS); 
    } 

} 

class ForLoo1 implements Callable<Object> { 

    @Override 
    public Object call() throws Exception { 

     System.out.println("I am writing content into file...."); 

     String s = "This is the content to write into a file"; 

     File file = new File("/home/f.txt"); 

     if (!file.exists()) { 
      file.createNewFile(); 
     } 

     FileWriter fw = new FileWriter(file); 
     BufferedWriter bw = new BufferedWriter(fw); 
     bw.write(s); 
     bw.close(); 
     System.out.println("Now you can read content from files..."); 
     return "ok"; 
    } 
} 

class ForLoop2 implements Callable<Object> { 

    private Future<Object> future; 

    public ForLoop2(Future<Object> future) { 
     this.future = future; 
    } 

    @Override 
    public Object call() throws Exception { 
     System.out.println("in ForLoop2, "); 
     Object ok = future.get(); 

     System.out.println("Okay i am now going to read content of files..."); 

     BufferedReader br = new BufferedReader(new FileReader("f.txt")); 
     String str; 
     while ((str = br.readLine()) != null) { 
      str = str + ""; 
     } 
     System.out.println("I am done with reading....."); 
     System.out.println(str); 
     return null; 
    } 
} 

また、あなたは、各呼び出し可能のコンストラクタに渡すことを、BlockingQueueのを使用することができます。私は、null以外の値を返すために、最初の呼び出し可能に変更しました。 1つのCallableはキューにエントリを配置し、もう1つはそこからエントリを読み込みます。これは、スレッド間で複数のメッセージが渡されることが予想される状況に適しています。ただし、同じ原則です。タスクにではなく、データ構造に待機/通知コードを挿入します。

関連する問題