2016-08-28 90 views
1

我使用ExecutorService創建了兩個獨立的線程。現在我只想讓一個線程將數據寫入一個文件,另一個線程在從將數據寫入文件的線程獲取通知之後將讀取它,但輸出沒有顯示任何內容,那麼如何才能實現我的目標。如何做兩個獨立的線程等待和通知?

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/javase/8/docs/api/java/util/concurrent/CountDownLatch.html) –

回答

-1

編輯:這是一個什麼不能做的例子。請使用CountDownLatch方法;在大多數程序中,您不需要使用notifywait。另外,閱讀下面的評論爲什麼這是一個壞主意。即使添加了​​塊(需要調用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

這樣更好,但仍然不正確(受到虛假喚醒的影響)。等待應始終在等待條件的循環中調用。 –

+1

另外,您需要保持monitorObject的鎖以調用wait並通知它。 –

+1

最重要的是,如果'ForLoop1'對象已經超過了'monitorObject.notify()'調用,那麼你需要保證'ForLoop2'對象將不會調用'monitorObject.wait()'。 OP的main()例程中的代碼不會保證'ForLoop1'任務總是首先被執行。 –

2

您可以通過下面的代碼達到預期效果。通過在ForLoop2中調用await,線程只會在ForLoop1中調用countDown後喚醒。 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是一個多功能的同步工具。它使ForLoop2的線程在ForLoo1完成後運行 – zpc

+0

在您的文章本身中添加上述信息。 –

0

您可以通過使用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,你傳遞到每個可調用的構造函數。一個Callable在隊列中放入一個條目,另一個從中讀取。這更適合您期望多個消息在線程之間傳遞的情況。但是,它的原理相同:將等待/通知代碼放入數據結構中,而不是放在任務中。