Каков наилучший способ достижения такого поведения потоков / событий в Java? - PullRequest
4 голосов
/ 05 марта 2010

У меня есть поток (Runnable), который запускает ряд других потоков (Runnables). Когда каждый дочерний поток завершает свою работу, он должен вызвать событие (или что-то подобное) и вернуть уведомление родительскому потоку. Я не вижу никаких событий в Java (ala C #) - я надеялся, что смогу просто подписаться в родительском объекте на событие «Я закончил» дочернего объекта, но, похоже, я не смогу это сделать. Как вы предлагаете мне это сделать?

Спасибо

Ответы [ 5 ]

4 голосов
/ 05 марта 2010

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

3 голосов
/ 05 марта 2010

Вы создаете интерфейс на родительском объекте

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 голосов
/ 05 марта 2010

Вы можете использовать вариант с шаблоном Observer .Реализуйте функцию обратного вызова (например, void finished(SomeArgs args)) в родительском элементе и создайте каждый дочерний элемент со ссылкой на его родительский элемент.Когда дочерний процесс завершится, пусть он вызовет родительский метод finished().

Убедитесь, что обратный вызов является поточно-ориентированным!

0 голосов
/ 11 августа 2010

Класс 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.
0 голосов
/ 05 марта 2010

Это не использует события, но я уверен, что это один из многих способов сделать это.Быстрое предостережение: чтобы это работало, вам нужно будет преобразовать ваш 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
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...