В ожидании темы - PullRequest
       10

В ожидании темы

4 голосов
/ 05 апреля 2010

У меня есть метод, который содержит следующий (Java) код:

doSomeThings();
doSomeOtherThings();

doSomeThings() создает несколько потоков, каждый из которых будет работать только ограниченное время. Проблема в том, что я не хочу, чтобы doSomeOtherThings() вызывался до тех пор, пока все потоки, запущенные doSomeThings() не будут завершены. (Также doSomeThings() будет вызывать методы, которые могут запускать новые потоки и т. Д. Я не хочу выполнять doSomeOtherThings(), пока все эти потоки не завершатся.)

Это потому, что doSomeThings() среди прочего установит myObject в null, в то время как doSomeOtherThings() вызывает myObject.myMethod(), и я не хочу, чтобы myObject было null в то время.

Есть ли какой-нибудь стандартный способ сделать это (в Java)?

Ответы [ 6 ]

10 голосов
/ 05 апреля 2010

Возможно, вы захотите взглянуть на пакет java.util.concurrent. В частности, вы можете рассмотреть возможность использования CountDownLatch как в

package de.grimm.game.ui;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {

public static void main(String[] args) 
throws Exception {

    final ExecutorService executor = Executors.newFixedThreadPool(5);
    final CountDownLatch latch = new CountDownLatch(3);

    for( int k = 0; k < 3; ++k ) {

        executor.submit(new Runnable() {
            public void run() {
                // ... lengthy computation...
                latch.countDown();
            }
        });
    }

    latch.await();

    // ... reached only after all threads spawned have
    // finished and acknowledged so by counting down the
    // latch.

    System.out.println("Done");
}
}

Очевидно, что этот метод будет работать, только если вы заранее знаете количество разветвленных нитей, поскольку вам нужно инициализировать защелку с этим числом.

Другой способ - использовать условные переменные, например:

boolean done = false;

void functionRunInThreadA() {

    synchronized( commonLock ) {

        while( !done ) commonLock.wait();
    }

    // Here it is safe to set the variable to null
}

void functionRunInThreadB() {

    // Do something...

    synchronized( commonLock ) {
        done = true;
        commonLock.notifyAll();
    }
}

Возможно, вам потребуется добавить обработку исключений (InteruptedException) и некоторые другие.

6 голосов
/ 05 апреля 2010

Взгляните на Thread.join() метод.

Я не совсем уверен в вашей точной реализации, но похоже, что doSomeThings () должен дождаться дочерних потоков перед возвратом.

Внутри метода doSomeThings () дождитесь потоков, вызвав метод Thread.join ().

Когда вы создаете поток и вызываете метод join () этого потока, вызывающий поток ожидает, пока этот объект потока не умрет.

Пример:

// Create an instance of my custom thread class
MyThread myThread = new MyThread();
// Tell the custom thread object to run
myThread.start();
// Wait for the custom thread object to finish
myThread.join();
3 голосов
/ 05 апреля 2010

Вы ищете это executorservice и используете фьючерсы:)

См. http://java.sun.com/docs/books/tutorial/essential/concurrency/exinter.html

Так что в основном собирайте фьючерсы для всех исполняемых файлов, которые вы отправляете в службу исполнителя. Зациклите все фьючерсы и вызовите методы get (). Они вернутся, когда будет создан соответствующий исполняемый файл.

1 голос
/ 06 апреля 2010

Еще один полезный, более надежный Синхронизирующий барьер , который вы можете использовать для выполнения функций, аналогичных CountdownLatch, - это CyclicBarrier . Он работает аналогично CountdownLatch, где вы должны знать, сколько сторон (потоков) используется, но он позволяет вам повторно использовать барьер для создания нового экземпляра CountdownLatch каждый раз.

Мне нравится предложение Momania использовать ExecutorService, собирать фьючерсы и вызывать их на всех, пока они не завершатся.

0 голосов
/ 09 апреля 2010

Зависит от того, что именно вы пытаетесь сделать здесь. Ваша главная проблема - способность динамически определять различные потоки, которые порождаются последовательными методами, которые вызываются из doSomeThings(), и затем иметь возможность ждать, пока они не завершатся, прежде чем вызвать doSomeOtherThings()? Или можно узнать потоки, которые создаются во время компиляции? В последнем случае существует несколько решений, но все они в основном включают вызов метода Thread.join() для всех этих потоков, откуда бы они ни создавались.

Если это действительно первое, то вам лучше использовать ThreadGroup и его enumerate() метод. Это дает вам массив всех потоков, созданных doSomeThings (), если вы правильно добавили новые потоки в группу нитей. Затем вы можете перебрать все ссылки на потоки в возвращенном массиве и вызвать join() в основном потоке непосредственно перед вызовом doSomeOtherThings().

0 голосов
/ 05 апреля 2010

Еще один вариант - перевести ваш основной поток в спящий режим и периодически проверять, завершены ли другие потоки. Однако мне больше нравятся ответы Дирка и Маркуса Адамса - просто выкидываю их сюда для полноты картины.

...