Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Многопоточность.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
378.88 Кб
Скачать

Метод join()

Метод join() блокирует работу потока, в котором он вызван, до тех пор, пока не будет закончено выполнение вызывающего метод потока.

Рассмотрим пример:

public class Example {

public static void main(String[] args)

throws InterruptedException{

Thread thread1 = new Thread (new ExampleRunnable(" B", 80));

Thread thread2 = new Thread (new ExampleRunnable("A", 130));

thread1.start();

thread1.join();

thread2.start();

}}

В примере сначала будут выведены 10 букв "B", а только потом 10 букв "А". Давайте разберемся почему так? В самом начале работы метода main() он запустит новый поток thread1, а после вызова метода thread1.join() привяжется к нему и будет ожидать его завершения. Как только поток thread1 завершит свою работу метод main() начнет выполняться дальше, а именно, запустит поток thread2 на выполнение.

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

Рассмотрим еще один пример. Запустим два потока. Один очень быстрый печатает букву "В", второй – медленный печатает букву "А".

public class Example {

public static void main(String[] args)

throws InterruptedException{

Thread thread1 = new Thread (new ExampleRunnable(" B", 80));

Thread thread2 = new Thread (new ExampleRunnable("A", 400));

thread1.start();

thread2.start();

thread1.join();

System.out.println("Start wait!");

thread2.join();

System.out.println("End!!!");

}}

B

B

B

B

A

B

B

B

B

B

A

B

Start wait!

A

A

A

A

A

A

A

A

End!!!

Поток метода main() привязывается к потоку thread1 и ждет его завершения. После чего, печатает Start wait! и привязывается к медленно выполняющемуся потоку thread2, ожидает его завершения и печатает End!!!

Как вы можете видеть метод join()вызывается для экземпляра класса Thread. А можно ли остановить другие потоки и дожидаться завершения потока метода main(), ведь у нас нет экземпляра данного потока (он стартуется автоматически JVM)? Оказывается, чтобы получить ссылку на тот поток, в котором мы находимся, следует вызвать статический метод currentThread() класса Thread.

Следующий код приведет к тому, что программа повиснет, так как метод main() будет ожидать завершения (смерти) самого себя. Такое явление в многопоточном программировании называется взаимная блокировка или dead lock.

public static void main(String[] args)

throws InterruptedException{

Thread thread = Thread.currentThread();

thread.join();

}

Достаточно записи вида: Thread.currentThread().join() и программа висит)))

Такая же ситуация может возникнуть если один поток привязывается к другому и ожидает его смерти, в то время как этот второй поток привязался к первому и тоже ожидает его смерти. В итоге имеем dead lock. Два потока ждут завершения друг друга.

Кто не понял – объясняю популярно…

«Заходя в ванную, Анжела забыла взять с собой халат. Обычно она может выйти в комнату и в неодетом виде, но, пока она была в ванной, в гости зашёл Антон, которому Анжела должна отдать флешку, которая лежит у неё в сумочке. Сам Антон в сумочку лезть отказывается, и требует, чтобы флешку отдала ему Анжела. Без флешки он не уйдёт. Анжела не может выйти в комнату пока там Антон. Антон ждёт, пока ему отдадут флешку, Анжела ждёт ухода Антона, после которого она может выйти и отдать флешку.»

Теперь понятно? Продолжаем)))

Dead lock можно разрешить путем использования условного метода join(long millis) с параметром, равным времени в миллисекундах, на которое нужно привязаться к потоку и ожидать его смерти. Если бы в примере было записано thread.join(1000), то через 1000 мс. поток метода main() отпустило бы и он продолжил свое выполнение.

Напишем класс, в котором поток метода main()и порожденный им второй поток создадут dead lock.

public class Example {

public static void main(String[] args)

throws InterruptedException{

final Thread mainThread = Thread.currentThread();

Thread runThread = new Thread (new Runnable(){

public void run() {

try{

System.out.println("Run: wait for main! ");

mainThread.join();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

});

runThread.start();

System.out.println("Main: wait for run! ");

runThread.join();

}}

Main: wait for run!

Run: wait for main!

Два потока будут привязаны к друг другу и это приведет к дидлоку. В каком порядке будет выведен текст предсказать трудно. Одна из особенностей потоков – это недетерменизм. Когда стартуют два потока невозможно предсказать, кто завершится раньше, кто первый, а кто последний. Они выполняются абсолютно автономно. Поэтому предсказать заранее, чей метод join() выполнится первым практически невозможно. Это иногда приводит к существенным трудностям программирования потоков в Java.

Обратите внимание на то, что в классе при создании второго потока runThread был использован анонимный inner класс, в котором реализован метод run(). Такая странная конструкция встречается в Java очень часто. Будем с ней сталкиваться при обработке событий. При запуске такой программы компилятор создаст анонимный класс, который будет имплементить интерфейс Runnable, и инстанс (instance – экземпляр) этого класса. Вы, конечно, помните, что нельзя создавать экземпляры абстрактных классов и интерфейсов, если у них не реализованы методы. В данном случае мы реализовываем единственный метод интерфейса Runnable – метод run().

В стандартном jdk нет возможности стартовать мгновенно много (целый пучок) потоков. Потоки могут порождаться только последовательно. Это же относится и к методу join(). Нельзя привязаться и ждать завершения сразу нескольких потоков – только по очереди. Однако в Java версии 5 был добавлен пакет java.util.concurrent, содержащий более 50 методов, которые расширяют возможности класса Thread по работе с потоками. Кого интересует - изучайте документацию)))