我有一个线程(Runnable),它启动了许多其他线程(Runnables)。当每个子线程完成时,它需要引发一个事件(或类似的东西)并向父线程返回一个通知。我在Java中看不到任何事件(ala C#) - 我曾希望我可以在父对象中订阅子对象的'我已经完成的事件'但是看起来我不能这样做。你怎么建议我做到这一点?
由于
答案 0 :(得分: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
答案 1 :(得分: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 );
}
}
答案 2 :(得分:2)
您可以在Observer pattern上使用变体。在父级中实现回调函数(例如void finished(SomeArgs args)
),并使用对其父级的引用构造每个子级。当孩子完成后,让它调用父母的finished()
方法。
确保回调是线程安全的!
答案 3 :(得分:0)
这不使用事件,但只是我确定很多方法来实现这一点。快速警告:要实现此功能,您需要将Runnable转换为Thread对象,或修改您的界面以在Runnable上使用某种isStopped()方法,无论您的Runnable是否仍在运行,都会返回该方法。 / p>
您可以让父线程跟踪List中的所有子线程。当子线程完成时,将它计算的值放在某个字段中,比如result,并创建一个名为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
}
答案 4 :(得分:0)
班级java.util.concurrent.ThreadPoolExecutor
可以满足您的需求。它执行多个线程并提供在每个Runnable
完成后调用的钩子。基本上,您可以创建一个匿名子类并覆盖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.