2010-03-05 40 views
4

我有一個線程(Runnable),啓動一些其他線程(Runnables)。當每個子線程完成時,它需要引發一個事件(或類似的東西)並向父線程返回一個通知。我無法在Java中看到任何事件(ala C#) - 我曾希望我只能在父對象中訂閱子對象的'我完成事件',但它不會出現我可以這樣做。你如何建議我完成這個?在Java中實現這種線程/事件行爲的最佳方式是什麼?

感謝

+4

您可以加入一個線程,但將阻塞,直到該線程的成品。 另一種選擇,如果您可以讓您的代碼使用Executor框架,則可以改爲執行其他Runnables任務。然後,您重寫'FutureTask'類的'done'方法(通過您編寫的子類)來指示完成任務。 – 2010-03-05 16:05:29

+2

@Chris Jester-Young:這應該是一個答案,而不是一個評論。 – 2010-03-05 16:06:26

+1

@MalcomTucker你應該使用CoutnDownLatch達到這個目的......查看我的答案以獲取更多細節。 – Kiril 2010-03-05 17:10:23

回答

2

您可以使用在Observer pattern的變體。在父項中實現回調函數(如void finished(SomeArgs args)),並通過對其父項的引用構造每個子項。當孩子完成時,讓它呼叫父母的方法finished()

確保回調是線程安全的!

3

你的父對象上創建一個接口

public interface EventListener { 
    void trigger(Object event); 
} 

public class Parent implements EventListener { 
    public synchronized void trigger(Object event) { 
     // process events. 
    } 
} 

public class Child implements Runnable { 
    private final EventListener listener; 

    public Child(EventListener listen) { 
     listener = listen; 
    } 

    public void run() { 
     //do stuff 
     listener.trigger(results); 
    } 
} 
+1

注意同步觸發方法 - 這很重要。而父類的其餘部分對於trigger()也必須是線程安全的。 – 2010-03-05 17:23:15

+1

當然,如果父母和孩子密切相關,這是矯枉過正;孩子可以直接瞭解Parent,並調用其中的一種方法。 – 2010-03-05 17:25:22

+1

CountDownLatch專門用於線程之間的通信:不需要鎖定。 – Kiril 2010-03-05 17:34:54

0

這不使用事件,但只是其中的一個我敢肯定,許多方法來做到這一點。快速警告:爲此,您需要將您的Runnable轉換爲Thread對象,或者修改您的接口以在您的Runnable上使用某種isStopped()方法,這將返回您的Runnable是否仍在運行。

您可以讓父線程跟蹤列表中的所有子線程。當一個子線程完成時,將它計算的值放在某個字段中,比如結果,然後創建一個名爲getResult()的方法。

父線程是否定期遍歷列表並檢查線程是否已停止。如果您將Runnable強制轉換爲一個Thread對象,則會有一個名爲isAlive()的方法來判斷線程是否已停止。如果有,請調用getResult()並執行任何操作。

在父線程,你可以這樣做:

Boolean running = true; 
while (running) { 
    //iterate through list 
    //if stopped, get value and do whatever 
    //if all the child threads are stopped, stop this thread and do whatever 
    Thread.sleep(1000); //makes this parent thread pause for 1 second before stopping again 
} 
+1

爲什麼要輪詢您何時可以推送信息? – danben 2010-03-05 16:12:21

+0

這只是一個選擇。 – 2010-03-05 16:13:55

+0

恩,絕對不想這樣做,我有可能數以千計的線程... – MalcomTucker 2010-03-05 16:19:02

4

Java有它的線程庫時CountDownLatch。創建一個CountDownLatch並使用您要運行的線程數來初始化它。當你創建你的線程時,你應該給它們閂鎖,每個線程在完成時都會發出信號。您的主線程將阻塞,直到所有工作線程完成。

隨着CountDownLatch你將實現與你的線程無鎖通信。

直接從Java的文檔:

class Driver { // ... 
    void main() throws InterruptedException { 
    CountDownLatch startSignal = new CountDownLatch(1); 
    CountDownLatch doneSignal = new CountDownLatch(N); 

    for (int i = 0; i < N; ++i) // create and start threads 
     new Thread(new Worker(startSignal, doneSignal)).start(); 

    doSomethingElse();   // don't let run yet 
    startSignal.countDown();  // let all threads proceed 
    doSomethingElse(); 
    doneSignal.await();   // wait for all to finish 
    } 
} 

class Worker implements Runnable { 
    private final CountDownLatch startSignal; 
    private final CountDownLatch doneSignal; 
    Worker(CountDownLatch startSignal, CountDownLatch doneSignal) { 
     this.startSignal = startSignal; 
     this.doneSignal = doneSignal; 
    } 
    public void run() { 
     try { 
     startSignal.await(); 
     doWork(); 
     doneSignal.countDown(); 
     } catch (InterruptedException ex) {} // return; 
    } 

    void doWork() { ... } 
} 

http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/CountDownLatch.html

0

java.util.concurrent.ThreadPoolExecutor做你所需要的。它執行多個線程並提供一個在每個Runnable完成後調用的鉤子。基本上,您可以創建一個annonymous子類並覆蓋afterExecute。就像這樣:

ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 20, 5, 
     TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(50)) { 
    @Override 
    protected void afterExecute(Runnable r, Throwable t) { 
     // do your callback stuff here 
    } 
}; 

下面是完整的例子:

import java.util.concurrent.ArrayBlockingQueue; 
import java.util.concurrent.ThreadPoolExecutor; 
import java.util.concurrent.TimeUnit; 

public class Main { 
    private static int ready = 0; 

    public static void main(String[] args) throws InterruptedException { 
     ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 20, 5, 
       TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(50)) { 
      @Override 
      protected void afterExecute(Runnable r, Throwable t) { 
       ready++; 
      } 
     }; 

     for (int n = 0; n < 5; n++) 
      executor.execute(createTask()); 
     executor.shutdown(); 

     while(ready < 5) { 
      System.out.println("Ready: " + ready); 
      Thread.sleep(100); 
     } 

     System.out.println("Ready with all."); 
    } 

    private static Runnable createTask() { 
     return new Runnable() { 
      @Override 
      public void run() { 
       try { 
        Thread.sleep((long) (Math.random() * 1000)); 
       } catch (InterruptedException e) { 
        // ignore exception to make debugging a little harder 
       } 
      } 
     }; 
    } 

} 

輸出是:

Ready: 0 
Ready: 1 
Ready: 1 
Ready: 3 
Ready: 3 
Ready: 4 
Ready: 4 
Ready: 4 
Ready: 4 
Ready with all. 
相關問題