在Java中实现这种线程/事件行为的最佳方法是什么?

时间:2010-03-05 16:01:18

标签: java multithreading events

我有一个线程(Runnable),它启动了许多其他线程(Runnables)。当每个子线程完成时,它需要引发一个事件(或类似的东西)并向父线程返回一个通知。我在Java中看不到任何事件(ala C#) - 我曾希望我可以在父对象中订阅子对象的'我已经完成的事件'但是看起来我不能这样做。你怎么建议我做到这一点?

由于

5 个答案:

答案 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.