Нередко требуется, чтобы главный поток исполнения завершался последним. С этой целью метод sleep() вызывался в предыдущих примерах из метода main() с достаточной задержкой, чтобы все дочерние потоки исполнения завершились раньше главного.
Но это неудовлетворительное решение, вызывающее следующий серьезный вопрос: откуда одному потоку исполнения известно, что другой поток завершился? Правда, в классе Thread предоставляется средство, позволяющее разрешить этот вопрос.
Определить, был ли поток исполнения завершен, можно двумя способами. Во первых, для этого потока можно вызвать метод isAlive(), определенный в классе Thread. Ниже приведена общая форма этого метода:
1 |
final Boolean isAlive() |
Метод isAlive() возвращает логическое значение true, если поток, для которого он вызван, еще исполняется. В противном случае он возвращает логическое значение false.
И во-вторых, в классе Thread имеется метод join(), который применяется чаще, чем метод isAlive(), чтобы дождаться завершения потока исполнения.
Ниже приведена общая форма этого метода:
1 |
final void join() throws InetrruptedException |
Этот метод ожидает завершения того потока исполнения, для которого он вызван. Его имя отражает следующий принцип: вызывающий поток ожидает, когда указанный поток присоединится к нему.
Дополнительные формы метода jоin() позволяют указывать максимальный промежуток времени , в течение которого требуется ожидать завершения указанного потока исполнения.
Ниже приведена усовершенствованная версия программы из предыдущего примера, где с помощью метода join() гарантируется, что главный поток завершится последним. В данном примере демонстрируется также применение метода isAlive().
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
// Применить метод join(), чтобы ожидать завершения потоков исполнения class NewThread implements Runnable { String name; // имя потоко исполнения Thread t; NewThread(String threadname) { name = threadname; t = new Thread(this, name); System.out.println("Новый поток: " + t); t.start(); // запустить поток исполнения } // Точка входа в поток исполнения public void run() { try { for(int i = 5; i > 0; i--) { System.out.println(name + ": " + i); Thread.sleep(1000); } } catch (InterruptedException e) { System.out.println(name + " прерван."); } System.out.println(name + " завершен."); } } class DemoJoin { public static void main(String args[]) { NewThread obj1 = new NewThread("Один"); NewThread obj2 = new NewThread("Два"); NewThread obj3 = new NewThread("Три"); System.out.println("Поток Один запущен: " + obj1.t.isAlive()); System.out.println("Поток Два запущен: " + obj2.t.isAlive()); System.out.println("Поток Три запущен: " + obj3.t.isAlive()); // ожидать завершения потоков исполнения try { System.out.println("Ожидание завершения потоков."); obj1.t.join(); obj2.t.join(); obj3.t.join(); } catch ( InterruptedException e ) { System.out.println("Главный поток прерван"); } System.out.println("Поток Один запущен: " + obj1.t.isAlive()); System.out.println("Поток Два запущен: " + obj2.t.isAlive()); System.out.println("Поток Три запущен: " + obj3.t.isAlive()); System.out.println("Главный поток завершен"); } } |
Ниже приведен результат, в ы водимый данной программой ( у вас он может оказаться иным в зависимости от конкретной исполняющей среды ).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
Новый поток: Thread[Один,5,main] Новый поток: Thread[Два,5,main] Один: 5 Новый поток: Thread[Три,5,main] Два: 5 Поток Один запущен: true Поток Два запущен: true Поток Три запущен: true Ожидание завершения потоков. Три: 5 Один: 4 Два: 4 Три: 4 Один: 3 Два: 3 Три: 3 Один: 2 Два: 2 Три: 2 Один: 1 Два: 1 Три: 1 Один завершен. Два завершен. Три завершен. Поток Один запущен: false Поток Два запущен: false Поток Три запущен: false Главный поток завершен |
Как видите, потоки прекращают исполнение после того, как вызовы метода join() возвращают управление.