在Java中实现这种线程/事件行为的最佳方法是什么?
我有一个线程(Runnable),它启动许多其他线程(Runnable)。当每个子线程完成时,它需要引发一个事件(或类似事件),并向父线程返回一个通知。我在Java(ala C#)中看不到任何事件-我希望我可以在父对象中订阅子对象的“我完成了事件”,但似乎我做不到。你建议我如何做到这一点在Java中实现这种线程/事件行为的最佳方法是什么?,java,multithreading,events,Java,Multithreading,Events,我有一个线程(Runnable),它启动许多其他线程(Runnable)。当每个子线程完成时,它需要引发一个事件(或类似事件),并向父线程返回一个通知。我在Java(ala C#)中看不到任何事件-我希望我可以在父对象中订阅子对象的“我完成了事件”,但似乎我做不到。你建议我如何做到这一点 谢谢您可以在上使用变体。在父级中实现回调函数(例如void finished(SomeArgs args)),并使用对其父级的引用构造每个子级。当子对象完成时,让它调用父对象的finished()方法 确保回调
谢谢您可以在上使用变体。在父级中实现回调函数(例如
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 );
}
}
这不使用事件,但我相信这只是实现这一点的许多方法之一。快速警告:要使其起作用,您需要将Runnable强制转换为Thread对象,或者修改接口以在Runnable上使用某种isStopped()方法,这将返回Runnable是否仍在运行 您可以让父线程在列表中跟踪其所有子线程。当一个子线程完成时,将它计算的值放在某个字段中,例如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
}
Java的线程库中有一个
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() { ... }
}
类驱动程序{/。。。
void main()抛出InterruptedException{
CountDownLatch startSignal=新的CountDownLatch(1);
CountDownLatch doneSignal=新的CountDownLatch(N);
for(inti=0;i
类
java.util.concurrent.ThreadPoolExecutor
执行您需要的操作。它执行许多线程,并提供一个钩子,在每次Runnable
完成后调用该钩子。基本上,您可以创建一个annonymous子类,并在执行后重写。像这样:
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
}
};
您可以加入一个线程,但这将阻止,直到该线程完成。如果可以让代码使用Executor框架,那么另一个选项是让其他可运行任务代替。然后,您重写类的FutureTask
方法(通过您编写的子类)以发出任务完成的信号。@Chris Jester Young:这应该是一个答案,而不是注释。@MalcomTucker您应该为此使用CoutnDownLatch。。。更多详细信息,请参见我的答案。@Aaron:通常我会将此作为答案发布,但我不想花时间(因为我正在工作:-P)查找Executor、FutureTask等的链接,这是我通常写答案的方式。所以,我想我应该给其他人一个快速的提示,让他们带着我跑。:-)(请随意把我的评论充实成一个真实的答案。我不介意。)不,我绝对不想这样做,我有潜在的数千个线程…注意同步触发器方法-这很重要。父类的其余部分对于trigger()也必须是线程安全的;子线程可以直接了解父线程并调用它的一个方法。CountDownLatch是专门为线程之间的通信而设计的:不需要锁定。
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.