Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java: Общие вопросы > Завершение главного потока после порожденных


Автор: nc30 18.9.2010, 05:42
Главный поток в цикле порождает несколько (количество заранее неизвестно) однотипных потоков, которые должны параллельно отработать. Главный поток должен дождаться завершения порожденных и вывести результаты их работы.
Если я делаю так:
Код

        // Генерация потоков для обработки некоторого кол-ва объектов.
        while ((obj = getObj()) != null) {

            MyThread mt = new MyThread(obj);
            mt.start();

            try {
                mt.join();
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
 то все порожденные потоки выполняются последовательно один за другим (проверял).

Другие варианты, которые пришли на мысль вместо использования join():
1) Поставить после цикла while: Thread.sleep(1500). В течении этого времени порожденные потоки успевают отработать. Ненадежно.
2) Записывать порожденные потоки в коллекцию threads. Затем после окончания while:
Код

        boolean smbLive;
        do {
            smbLive = false;
            for (Thread thr : threads) {
                if (thr.isAlive()) {
                    smbLive = true;
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                }
            }
        } while (smbLive);
 Это работает, но здесь приходится создавать дополнительную коллекцию. Пока остановился на этом варианте.
3) Задать главному потоку более низкий приоритет, но здесь не будет 100% гарантии.

Как лучше (и, желательно, проще) поступить?  smile 


Цитата(jk1 @ 17.9.2010,  18:55)
как насчет использования http://download.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/CyclicBarrier.html?
Спасибо, попробую разобраться  smile 

Автор: dobrolub 18.9.2010, 06:40
Тесты для thread scheduling довольно сложная вещь. Ты правильно пишешь, что надо использовать join. Но тут ещё надо тест правильно написать , чтобы нигде в синхронизированный объект не попасть, и т.д. Для некоторых машин надо ставить yield , чтобы дать возможность threadam поменяться.

посмотри на код и позапускай:
Код

import java.util.*;

public class T implements Runnable {
    private final static int size = 5;
    private volatile boolean stop = false;

    private ArrayList<Thread> _list = new ArrayList<Thread>(size);

    public void run() {
    while (! stop) {
        Random r = new Random();
        int i = r.nextInt(5);
        Thread thread = Thread.currentThread();
        synchronized(_list) {
        _list.set(i, thread);
        }
        thread.yield();
    }
    }

    public static void main(String []args) {
    final T t = new T();

    Thread[] threads = new Thread[size];
    for (int i = 0; i < t.size; i++) {
        Thread thread = new Thread(t);
        thread.setName("thread: " + i);
        threads[i] = thread;
        t._list.add(i, thread);
    }

    for (Thread thread: threads) 
        thread.start();

    Thread statePrinter = new Thread() {
        public void run() {
            while(true) {
            synchronized(t._list) {
                System.out.println(t._list);
            }
            Random r = new Random();
            int sleep = r.nextInt(10) * 100;

            if (sleep == 500)
                t.stop = true;
            
            try {
                Thread.sleep(sleep);
            } catch (Exception e) {}
            }
        }
    };
    statePrinter.setDaemon(true);

    statePrinter.start();

    for (Thread thread: threads) {
        try {
        thread.join();
        } catch (Exception e) {//don't care
        e.printStackTrace();
        }
    }
    }

}

Автор: nc30 24.9.2010, 13:42
jk1
CyclicBarrier мне не подойдет, т. к. заранее неизвестно количество порождаемых потоков. Но за хорошую идею спасибо.

dobrolub
Уровень приведенного вами кода для меня пока высоковат.

Пока остановился на варианте цикла в main thread по списку порожденных потоков с вызовом isAlive() и sleep(). Не очень элегантно, зато простенько.

Благодарю за помощь  smile 

Автор: LSD 24.9.2010, 15:31
Цитата(nc30 @  24.9.2010,  14:42 Найти цитируемый пост)
Пока остановился на варианте цикла в main thread по списку порожденных потоков с вызовом isAlive() и sleep(). Не очень элегантно, зато простенько.

isAlive() и sleep() можно заменить на один join().

Автор: nc30 24.9.2010, 15:55
Цитата(LSD @ 24.9.2010,  13:31)
isAlive() и sleep() можно заменить на один join().

Пробовал - не то.
Код

   // main thread
    public void checkRequests() {
        RequestStorageService rsService = RequestStorageService.getInstance();
        ServiceClient sc = null;
        
        while ((sc = rsService.getUncheckedClient()) != null) {
            // создание нового потока для проверки данных клиента, полученного от сторонней функции
            CheckingThread chThread = new CheckingThread(sc, rsService);
            chThread.start();
            chThread.join();
        }
    }

В этом случае порожденные потоки (CheckingThread) просто исполняются последовательно один за другим. Проверял.
Пришлось сделать вот так:
Код

    public void checkRequests() {
        RequestStorageService rsService = RequestStorageService.getInstance();
        ArrayList<Thread> threads = new ArrayList<Thread>();
        ServiceClient sc = null;

        while ((sc = rsService.getUncheckedClient()) != null) {
            CheckingThread chThread = new CheckingThread(sc, rsService);
            threads.add(chThread);
            chThread.start();
            
        }

        // Проверка всех порожденных потоков на завершение перед продолжением главного.
        boolean smbAlive;
        do {
            smbAlive = false;
            for (Thread thr : threads) {
                if (thr.isAlive()) {
                    smbAlive = true;
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                }
            }
        } while (smbAlive);
    }

Может я join() неправильно использую?..

Автор: Skipy 24.9.2010, 16:14
Цитата(nc30 @ 24.9.2010,  15:55)
Может я join() неправильно использую?..

Запустить потоки, сложить в коллекцию. Потом в цикле по коллекции на каждый делать join().

Автор: XoJIoD 24.9.2010, 16:21
Цитата(nc30 @  18.9.2010,  05:42 Найти цитируемый пост)
Главный поток в цикле порождает несколько (количество заранее неизвестно) однотипных потоков, которые должны параллельно отработать. Главный поток должен дождаться завершения порожденных и вывести результаты их работы.


Если надо получить результат работы дочерних потоков может лучше воспользоваться интерфейсом Callable.

Код

public class MyCallable implements Callable<BigInteger> {
    
    public static final Random r = new Random();
    
    @Override
    public BigInteger call() throws Exception {
        return BigInteger.valueOf(r.nextInt());
    }

}

public class MyTest {
    public static void main(String[] args) {
        List<Future<BigInteger>> result = new LinkedList<Future<BigInteger>>();
        ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++)
            result.add(newCachedThreadPool.submit(new MyCallable()));
        while (!result.isEmpty()) {
            try {
                for (Iterator<Future<BigInteger>> iterator = result.iterator(); iterator
                        .hasNext();) {
                    Future<BigInteger> future = iterator.next();
                    if (future.isDone()) {
                        System.out.println(future.get().toString());
                        iterator.remove();
                    }
                }
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
}

Автор: nc30 24.9.2010, 18:16
Цитата(Skipy @ 24.9.2010,  14:14)
Запустить потоки, сложить в коллекцию. Потом в цикле по коллекции на каждый делать join().

 smile Спасибо, Skipy, теперь все работает так как надо.

Всем спасибо за помощь.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)