我有一個線程(Runnable),啓動一些其他線程(Runnables)。當每個子線程完成時,它需要引發一個事件(或類似的東西)並向父線程返回一個通知。我無法在Java中看到任何事件(ala C#) - 我曾希望我只能在父對象中訂閱子對象的'我完成事件',但它不會出現我可以這樣做。你如何建議我完成這個?在Java中實現這種線程/事件行爲的最佳方式是什麼?
感謝
我有一個線程(Runnable),啓動一些其他線程(Runnables)。當每個子線程完成時,它需要引發一個事件(或類似的東西)並向父線程返回一個通知。我無法在Java中看到任何事件(ala C#) - 我曾希望我只能在父對象中訂閱子對象的'我完成事件',但它不會出現我可以這樣做。你如何建議我完成這個?在Java中實現這種線程/事件行爲的最佳方式是什麼?
感謝
您可以使用在Observer pattern的變體。在父項中實現回調函數(如void finished(SomeArgs args)
),並通過對其父項的引用構造每個子項。當孩子完成時,讓它呼叫父母的方法finished()
。
確保回調是線程安全的!
你的父對象上創建一個接口
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);
}
}
注意同步觸發方法 - 這很重要。而父類的其餘部分對於trigger()也必須是線程安全的。 – 2010-03-05 17:23:15
當然,如果父母和孩子密切相關,這是矯枉過正;孩子可以直接瞭解Parent,並調用其中的一種方法。 – 2010-03-05 17:25:22
CountDownLatch專門用於線程之間的通信:不需要鎖定。 – Kiril 2010-03-05 17:34:54
這不使用事件,但只是其中的一個我敢肯定,許多方法來做到這一點。快速警告:爲此,您需要將您的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
}
爲什麼要輪詢您何時可以推送信息? – danben 2010-03-05 16:12:21
這只是一個選擇。 – 2010-03-05 16:13:55
恩,絕對不想這樣做,我有可能數以千計的線程... – MalcomTucker 2010-03-05 16:19:02
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
類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.
您可以加入一個線程,但將阻塞,直到該線程的成品。 另一種選擇,如果您可以讓您的代碼使用Executor框架,則可以改爲執行其他Runnables任務。然後,您重寫'FutureTask'類的'done'方法(通過您編寫的子類)來指示完成任務。 – 2010-03-05 16:05:29
@Chris Jester-Young:這應該是一個答案,而不是一個評論。 – 2010-03-05 16:06:26
@MalcomTucker你應該使用CoutnDownLatch達到這個目的......查看我的答案以獲取更多細節。 – Kiril 2010-03-05 17:10:23