Что означает этот код присоединения к потоку?


156

В этом коде, что означают два соединения и разрыв? t1.join()вызывает, t2чтобы остановить, пока не t1закончится?

Thread t1 = new Thread(new EventThread("e1"));
t1.start();
Thread t2 = new Thread(new EventThread("e2"));
t2.start();
while (true) {
   try {
      t1.join();
      t2.join();
      break;
   } catch (InterruptedException e) {
      e.printStackTrace();
   }
}


3
Так как он будет блокировать и ждать завершения потока, почему вы использовали цикл while?
Мехди

@MahdiElMasaoudi Полагаю, продолжать ждать, даже если поток прерывается? Вероятно, не
лучший

Не забудьте принять ответ здесь, если это было полезно.
Серый

Как уже упоминалось, вам не нужно while(true)вызывать joinметод.
Chaklader Asfak Arefe

Ответы:


311

Что означает этот код присоединения к потоку?

Цитировать из Thread.join()метода Javadocs :

join() Ждет, когда эта нить умрет.

Существует поток, который выполняет ваш пример кода, который, вероятно, является основным потоком .

  1. Основной поток создает и запускает t1и t2тему. Два потока начинают работать параллельно.
  2. Основной поток вызывает t1.join()ожидание завершения t1потока.
  3. Поток t1завершается, и t1.join()метод возвращается в основной поток. Обратите внимание, что t1он уже мог закончиться до join()того, как будет выполнен вызов, и в этом случае join()вызов будет немедленно возвращен.
  4. Основной поток вызывает t2.join()ожидание завершения t2потока.
  5. Поток t2завершается (или мог завершиться раньше, чем t1поток), и t2.join()метод возвращается в основной поток.

Важно понимать, что потоки t1and t2работали параллельно, но основному потоку, который их запустил, нужно дождаться их завершения, прежде чем он сможет продолжить. Это обычная модель. Кроме того, t1и / или t2мог завершиться до того, как основной поток вызовет join()их. Если это так, то join()не будет ждать, но вернется немедленно.

t1.join() означает, что t2 останавливается до тех пор, пока не закончится t1?

Нет. Основной вызывающий поток t1.join()прекратит работу и будет ждать t1его завершения. Поток t2работает параллельно и не подвержен влиянию t1илиt1.join() вызову вообще.

С точки зрения try / catch, join()throws InterruptedExceptionозначает, что основной поток, который вызывает, join()может сам быть прерван другим потоком.

while (true) {

Наличие соединений в whileцикле - странная картина. Как правило, вы выполняете первое соединение, а затем второе соединение, обрабатывая InterruptedExceptionсоответственно в каждом случае. Не нужно их зацикливать.


24
+1 Это очень странная картина и, вероятно, ее можно убрать.
m0skit0

3
Если t1 финиширует первым, то t2 финиширует. Это похоже на последовательный процесс. Сначала заканчивается один поток, затем другой. какой смысл многопоточности?
user697911

9
Потому что t1и t2может работать параллельно. Просто им mainнужно, чтобы они оба закончили, прежде чем продолжить. Это типичный шаблон @ user697911.
Серый,

3
whileПетля там , потому что (я думаю) он хочет повторить те join()вызовы , если один прерывается? Я бы точно так не написал @ user697911.
Серый,

5
Цикл есть для того, чтобы и то, t1и другое t2закончилось. То есть. если t1выкинет InterruptedException, он вернется и будет ждать t2. Альтернативой является ожидание обоих потоков в каждом их Try-Catch, чтобы избежать цикла. Кроме того, в зависимости от EventThreadэтого может иметь смысл сделать это таким образом, так как мы запускаем 2 потока, а не один.
Майкл Бисбьерг

68

Это любимый вопрос интервью на Java .

Thread t1 = new Thread(new EventThread("e1"));
t1.start();
Thread e2 = new Thread(new EventThread("e2"));
t2.start();

while (true) {
    try {
        t1.join(); // 1
        t2.join(); // 2  These lines (1,2) are in in public static void main
        break;
    }
}

t1.join()означает, что t1 говорит что-то вроде « Я хочу закончить первым ». То же самое в случае с t2. Независимо от того, кто начал t1или в t2каком потоке (в данном случае это mainметод), main будет ждать, пока t1и не t2завершит свою задачу.

Тем не менее, важно отметить, t1и t2сами могут работать параллельно, независимо от последовательности вызовов соединения на t1и t2. Это main/daemonнить, которая должна ждать .


3
Хороший пример. Про "может работать параллельно": ну и что? Важно, что основной поток будет ждать ПЕРВЫЙ для t1 и ТОГДА для t2. Это действительно не имеет значения, что делают t1 или t2 (с точки зрения основного потока)
Alex

1
Вы упоминаете тему "main / daemon". Главное, я понимаю, но демон не имеет к этому никакого отношения. Основной поток не является демоном.
Серый

1
t1.join(); t2.join();не позволит потоку, который выполняет соединения, продолжаться, пока оба потока не завершатся. При отсутствии очень необычного кода в другом месте порядок соединений не имеет значения.
Дэвид Шварц

Так будет ли вызываться t2.join () только после окончания t1?
Лев Droidcoder

Другими словами, если мы хотим «сериализовать» выполнение потоков t1 и t2, нам нужно поместить t1.join () сразу после t1.start (), поскольку основной поток начал t1 (и впоследствии t2), и, конечно, удалить его из попробуй поймать. Очевидно, что при этом последствием будет потеря параллелизма.
Добровое

47

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


29

Одна картинка стоит тысячи слов.

    Main thread-->----->--->-->--block##########continue--->---->
                 \                 |               |
sub thread start()\                | join()        |
                   \               |               |
                    ---sub thread----->--->--->--finish    

Надеюсь на полезное, для более подробной информации нажмите здесь


3
Ясно и точно.
Добривое

10

Когда поток tA вызывает tB.join (), его причины не только ждут смерти tB или самого прерывания tA, но и создают случайное отношение между последним оператором в tB и следующим оператором после tB.join () в потоке tA.

Все действия в потоке происходят до того, как любой другой поток успешно вернется из join () в этом потоке.

Значит программа

class App {
    // shared, not synchronized variable = bad practice
    static int sharedVar = 0;
    public static void main(String[] args) throws Exception {
        Thread threadB = new Thread(() -> {sharedVar = 1;});
        threadB.start();
        threadB.join();

        while (true) 
            System.out.print(sharedVar);
    }
}

Всегда печатать

>> 1111111111111111111111111 ...

Но программа

class App {
    // shared, not synchronized variable = bad practice
    static int sharedVar = 0;
    public static void main(String[] args) throws Exception {
        Thread threadB = new Thread(() -> {sharedVar = 1;});
        threadB.start();
        // threadB.join();  COMMENT JOIN

        while (true)
            System.out.print(sharedVar);
    }
}

Можно печатать не только

>> 0000000000 ... 000000111111111111111111111111 ...

Но

>> 00000000000000000000000000000000000000000000 ... 

Всегда только «0».

Поскольку Java Memory Model не требует «передачи» нового значения sharedVar из threadB в основной поток без отношения heppens-before (начало потока, объединение потока, использование ключевого слова synchronized, использование переменных AtomicXXX и т. Д.).


5

Проще говоря:
t1.join()возвращается после t1завершения.
Он ничего не делает с потоком t1, кроме как дождаться его завершения.
Естественно, следующий код t1.join()будет выполняться только после t1.join()возврата.


1
будет выполнено только после того, как t1.join () вернет +1
mohsen.nour

3

Со страницы документации оракула в Joins

joinМетод позволяет один потоку ждать завершения других.

Если t1 является Threadобъектом, поток которого выполняется в данный момент,

t1.join() : causes the current thread to pause execution until t1's thread terminates.

Если t2 является Threadобъектом, поток которого выполняется в данный момент,

t2.join(); causes the current thread to pause execution until t2's thread terminates.

joinAPI - это API низкого уровня, который был представлен в более ранних версиях Java. Многие вещи были изменены в течение определенного периода времени (особенно с выпуском jdk 1.5) на фронте параллелизма.

Вы можете достичь того же с помощью API java.util.concurrent. Некоторые из примеров

  1. Использование invokeAll наExecutorService
  2. Использование CountDownLatch
  3. Используя ForkJoinPool или newWorkStealingPool из Executors(начиная с Java 8)

Обратитесь к связанным вопросам SE:

ждать, пока все потоки не закончат свою работу в Java


1

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

Внизу это чистый механизм wait () и notify ().

Все мы знаем, что когда мы вызываем wait () для любого объекта (t1), вызывающий объект (main) отправляется в комнату ожидания (состояние Blocked).

Здесь основной поток вызывает join (), который wait () под прикрытием. Таким образом, основной поток будет ждать, пока не получит уведомление. Уведомление дается t1, когда он заканчивает свою работу (завершение потока).

После получения уведомления главный выходит из комнаты ожидания и приступает к его исполнению.


0

Надеюсь, поможет!

package join;

public class ThreadJoinApp {

    Thread th = new Thread("Thread 1") {
        public void run() {
            System.out.println("Current thread execution - " + Thread.currentThread().getName());
            for (int i = 0; i < 10; i++) {
                System.out.println("Current thread execution - " + Thread.currentThread().getName() + " at index - " + i);
            }
        }
    };

    Thread th2 = new Thread("Thread 2") {
        public void run() {
            System.out.println("Current thread execution - " + Thread.currentThread().getName());

            //Thread 2 waits until the thread 1 successfully completes.
            try {
            th.join();
            } catch( InterruptedException ex) {
                System.out.println("Exception has been caught");
            }

            for (int i = 0; i < 10; i++) {
                System.out.println("Current thread execution - " + Thread.currentThread().getName() + " at index - " + i);
            }
        }
    };

    public static void main(String[] args) {
        ThreadJoinApp threadJoinApp = new ThreadJoinApp();
        threadJoinApp.th.start();
        threadJoinApp.th2.start();
    }

    //Happy coding -- Parthasarathy S
}

-3

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

Итак, вот как это работает.

Кроме того, цикл while здесь не нужен.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.