Концепция многопоточности в Java и метод join()

Я запутался в методе join(), используемом в потоках в Java. В следующем коде:

// Using join() to wait for threads to finish.
class NewThread implements Runnable {

    String name; // name of thread
    Thread t;

    NewThread(String threadname) {
        name = threadname;
        t = new Thread(this, name);
        System.out.println("New thread: " + t);
        t.start(); // Start the thread
    }
// This is the entry point for thread.

    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 + " interrupted.");
        }
        System.out.println(name + " exiting.");
    }
}

class DemoJoin {

    public static void main(String args[]) {
        NewThread ob1 = new NewThread("One");
        NewThread ob2 = new NewThread("Two");
        NewThread ob3 = new NewThread("Three");
        System.out.println("Thread One is alive: "
                + ob1.t.isAlive());
        System.out.println("Thread Two is alive: "
                + ob2.t.isAlive());
        System.out.println("Thread Three is alive: "
                + ob3.t.isAlive());
// wait for threads to finish
        try {
            System.out.println("Waiting for threads to finish.");
            ob1.t.join();
            ob2.t.join();
            ob3.t.join();
        } catch (InterruptedException e) {
            System.out.println("Main thread Interrupted");
        }
        System.out.println("Thread One is alive: "
                + ob1.t.isAlive());
        System.out.println("Thread Two is alive: "
                + ob2.t.isAlive());
        System.out.println("Thread Three is alive: "
                + ob3.t.isAlive());
        System.out.println("Main thread exiting.");
    }
}

Пример вывода этой программы показан здесь:

New thread: Thread[One,5,main]
New thread: Thread[Two,5,main]
New thread: Thread[Three,5,main]
Thread One is alive: true
Thread Two is alive: true
Thread Three is alive: true
Waiting for threads to finish.
One: 5
Two: 5
Three: 5
One: 4
Two: 4
Three: 4
One: 3
Two: 3
Three: 3
One: 2
Two: 2
Three: 2
One: 1
Two: 1
Three: 1
Two exiting.
Three exiting.
One exiting.
Thread One is alive: false
Thread Two is alive: false
Thread Three is alive: false
Main thread Exiting

В приведенном выше коде:

  1. Я не могу понять поток выполнения программы, и когда ob1 создается, вызывается конструктор, где t.start() пишется, но все же метод run() не выполняется, а метод main() продолжает выполнение. Так почему это происходит?

  2. join() используется для ожидания, пока поток, в котором он вызывается, не завершится, но здесь в выводе мы видим альтернативные выводы потока, почему??

И если использование join это то, что использование synchronized??

Я знаю, что мне не хватает базовой концепции, но я не могу понять это, поэтому, пожалуйста, помогите.


person user2696258    schedule 28.08.2013    source источник
comment
«Я не могу понять ход выполнения программы» — хорошо, это нормально для многопоточности. Хитрость заключается в том, чтобы спроектировать так, чтобы это не имело значения.   -  person Martin James    schedule 28.08.2013
comment
Вы должны запустить эту программу в режиме отладки, вы лучше поймете концепцию join(). На самом деле трудно предсказать правильный порядок. Поэтому запустите его в режиме отладки.   -  person Akash5288    schedule 24.12.2013
comment
вызов start из конструктора - плохая идея, возможно, объект еще не сформирован.   -  person David Prun    schedule 23.10.2014
comment
Думайте об этом как о блокировке параллельного выполнения. Любые последующие операторы будут выполняться только после выполнения потоков joined.   -  person peterchaula    schedule 20.11.2016


Ответы (12)


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

Однако вы можете использовать join(), чтобы дождаться завершения работы потока.

Например, в вашем случае

ob1.t.join();

Этот оператор не вернется, пока поток t не завершит выполнение.

Попробуй это,

class Demo {
   Thread t = new Thread(
                 new Runnable() {
                     public void run () {
                         //do something
                     }
                  }
    );
    Thread t1 = new Thread(
                 new Runnable() {
                     public void run () {
                         //do something
                     }
                  }
    );
    t.start(); // Line 15
    t.join();  // Line 16
    t1.start();
}

В приведенном выше примере выполняется ваш основной поток. Когда он встречает строку 15, поток t доступен в планировщике потоков. Как только основной поток дойдет до строки 16, он будет ждать завершения потока t.

ЗАМЕТЬТЕ, что t.join ничего не сделал с потоком t или с потоком t1. Это затронуло только поток, вызвавший его (т. е. поток main()).

Отредактировано:

t.join(); должен находиться внутри блока try, потому что это throws исключение InterruptedException, иначе вы получите ошибку во время компиляции. Итак, должно быть:

try{
    t.join();
}catch(InterruptedException e){
    // ...
}
person Malwaregeek    schedule 28.08.2013
comment
Я искал в Интернете простой ответ, который объяснит эту концепцию ПРОСТО! почти час. но это единственный ответ, который я нашел, который пошел прямо к делу, самым простым способом. @malwaregeek, спасибо. - person kc ochibili; 14.02.2014
comment
Что произойдет, если t.join() будет удален (строка 16), а t1.join будет добавлен после t1.start()? - person 6055; 15.10.2014
comment
Отличное объяснение! :) - person gaurav jain; 05.04.2015
comment
если вызывающие потоки переходят в спящий режим, будет ли выполняться текущий поток? - person kapil; 13.01.2016
comment
@Malwaregeek будет ли основной поток выполняться снова после того, как поток t1 перейдет в состояние wait. или он запустится только после полного выполнения thread t1 - person Kasun Siyambalapitiya; 11.08.2016
comment
Чего я не понимаю, так это того, что такое текущий поток, когда есть два запущенных потока? Начиная с line 15 у вас запущены и t, и main. Когда вы говорите join() в line 16, текущий поток должен быть остановлен, но t был запущен последним. А как насчет того, когда у вас есть 3 потока, работающих после line 17? Какой из них текущий? - person Cornelius; 19.01.2017
comment
Смущен тем, что t1.start() с t1 запускается с использованием потока main, он не должен запускаться, если поток t не завершен, поскольку мы используем t.join. - person Krishna Oza; 02.02.2017
comment
Я отредактировал этот ответ, чтобы удалить слова текущего потока. Всякий раз, когда вы видите эти слова в документации, они не говорят о текущем запущенном потоке!! (Какой поток это будет в программе, которая имеет много потоков, работающих на многоядерной машине?) Текущий поток на самом деле означает поток, который вызывает функцию. Thread.currentThread() возвращает ссылку на объект Thread, управляющий вызывающим. Thread.sleep(n) — это просто функция, которая ждет n миллисекунды перед возвратом, t.join() — это просто функция, которая ждет, когда поток t умрет, и т. д. - person Solomon Slow; 03.04.2017
comment
последнее примечание объяснило все! - person Kaushal28; 19.08.2017
comment
что произойдет, если я использую t.join() после t1.start()? - person Neeraj Yadav; 30.12.2017
comment
Последнее замечание — отличное объяснение. Спасибо! - person Ercan Akkök; 21.11.2018

Прежде всего, когда вы создаете ob1, вызывается конструктор, и он начинает выполнение. В это время t.start() также работает в отдельном потоке. Помните, что когда создается новый поток, он выполняется параллельно основному потоку. И именно поэтому main снова запускает выполнение со следующего оператора.

А оператор Join() используется для предотвращения того, чтобы дочерний поток стал потерянным. Это означает, что если вы не вызвали join() в своем основном классе, то основной поток завершится после его выполнения, а дочерний поток все еще будет выполняться. заявления. Join() будет ждать, пока все дочерние потоки завершат свое выполнение, после чего завершится только основной метод.

Прочтите эту статью, она очень поможет.

person Vimal Bera    schedule 28.08.2013
comment
Значит, если нет join(), то One Two Three... выходная часть не придет ?? - person user2696258; 28.08.2013
comment
нет..не то. Он показывает вывод Ont two three.. но в середине one two three..Main Thread Existing будет отображаться, потому что основной поток завершает свое выполнение и не имеет ничего общего с дочерним потоком. - person Vimal Bera; 28.08.2013

Я не могу понять поток выполнения программы, и когда создается ob1, вызывается конструктор, где записывается t.start(), но метод run() не выполняется, а метод main() продолжает выполнение. Так почему это происходит?

Это зависит от планировщика потоков, так как main имеет одинаковый порядок приоритетов. Вызов start() не означает, что run() будет вызван немедленно, это зависит от планировщика потоков, когда он решит запустить ваш поток.

Метод join() используется для ожидания, пока поток, в котором он вызывается, не завершится, но здесь в выводе мы видим альтернативные выводы потока, почему??

Это из-за Thread.sleep(1000) в вашем коде. Удалите эту строку, и вы увидите, что ob1 завершается до ob2, который, в свою очередь, завершается до ob3 (как и ожидалось с join()). Сказав, что все зависит от того, когда начались ob1 ob2 и ob3. Вызов sleep приостановит выполнение потока на >= 1 секунду (в вашем коде), давая планировщику возможность вызвать другие ожидающие потоки (с таким же приоритетом).

person rmishra    schedule 25.02.2014

Первое правило треда - "Триддинг - это весело"...

Я не могу понять поток выполнения программы, и когда создается ob1, вызывается конструктор, где записывается t.start(), но все же метод run() не выполняется, а метод main() продолжает выполнение. Так почему это происходит?

Это именно то, что должно произойти. Когда вы вызываете Thread#start, поток создается и назначается для выполнения, это может произойти немедленно (или достаточно близко к этому), а может и нет. Это сводится к планировщику потоков.

Это сводится к тому, как запланировано выполнение потока и что еще происходит в системе. Как правило, каждому потоку будет дано небольшое количество времени для выполнения, прежде чем он будет переведен в «спящий режим» и будет разрешено выполнение другого потока (очевидно, что в многопроцессорных средах одновременно может выполняться более одного потока, но давайте попробуем и будь проще))

Потоки также могут yield выполняться, позволяя другим потокам в системе иметь возможность выполняться.

Вы могли бы попробовать

NewThread(String threadname) {
    name = threadname;
    t = new Thread(this, name);
    System.out.println("New thread: " + t);
    t.start(); // Start the thread
    // Yield here
    Thread.yield();
}

И это может повлиять на то, как работают потоки... в равной степени вы можете sleep в течение небольшого периода времени, но это может привести к тому, что ваш поток будет пропущен для выполнения в течение периода циклов (иногда вам нужно это, иногда ты нет)...

join() используется для ожидания, пока поток, в котором он вызывается, не завершится, но здесь в выводе мы видим альтернативные выводы потока, почему??

То, как вы задали вопрос, неверно ... join будет ждать, пока Thread, к которому он призван, умрет, прежде чем вернуться. Например, если вы зависите от результата Thread, вы можете использовать join, чтобы узнать, когда закончилось Thread, прежде чем пытаться получить его результат.

В равной степени вы можете опросить поток, но это будет потреблять циклы ЦП, которые вместо этого лучше использовать Thread...

person MadProgrammer    schedule 28.08.2013
comment
В основном. Это потребовало бы цикла, который периодически проверял бы, конкурировал ли поток или нет. Может ли он использовать yield или sleep. Но для этого потребуется, чтобы у потока было время для выполнения, что отнимает время у других потоков. - person MadProgrammer; 29.08.2013

JVM и базовая ОС имеют значительную свободу при планировании. Тот факт, что вы проходите весь путь до «Ожидания завершения потоков», прежде чем увидите выходные данные отдельных потоков, может просто означать, что запуск потока занимает немного больше времени (т. е. проходит некоторое время между моментом, когда поток становится « live" и когда метод run() действительно начинает выполняться). Возможно, вы могли бы увидеть вывод потока раньше, но в любом случае это не гарантируется.

Что касается join(), он гарантирует только то, что все, что будет после него, произойдет только после завершения потока, к которому вы присоединяетесь. Таким образом, если у вас есть три вызова join() подряд, это не означает, что потоки должны заканчиваться в определенном порядке. Это просто означает, что сначала вы будете ждать ob1. После завершения ob1 ob2 и ob3 могут продолжать выполняться или уже быть завершены. Если они завершены, другие ваши вызовы join() немедленно вернутся.

synchronized используется, когда несколько потоков обращаются к одному и тому же объекту и вносят в него изменения. Гарантируется, что синхронизированный блок никогда не будет выполняться двумя потоками одновременно, т. е. поток, который его выполняет, имеет весь синхронизированный объект для себя.

person codingjourney    schedule 28.08.2013

когда создается ob1, вызывается конструктор, где написано «t.start()», но метод run() все равно не выполняется, а метод main() выполняется дальше. Так почему это происходит?

здесь ваши потоки и основной поток имеют одинаковый приоритет. Выполнение потока с равным приоритетом полностью зависит от Thread schedular. Вы не можете ожидать, что будет выполняться первым.

Метод join() используется для ожидания, пока поток, в котором он вызывается, не завершится, но здесь в выводе мы видим альтернативные выводы потока, почему??

Здесь вы вызываете ниже утверждения из основного потока.

     ob1.t.join();
     ob2.t.join();
     ob3.t.join();

Таким образом, основной поток ожидает завершения ob1.t, ob2.t, ob3.t потоков (загляните в Thread#join doc). Таким образом, все три потока выполняются успешно, а основной поток завершается после этого.

person Prabhaker A    schedule 28.08.2013

Мои комментарии:

Когда я вижу вывод, вывод смешивается с One, Two, Three, которые являются именами потоков, и они выполняются одновременно. Я не уверен, когда вы говорите, что поток не работает по основному методу.

Не уверен, понял ли я ваш вопрос или нет. Но я отвечаю так, как могу понять, надеюсь, это поможет вам.

1) Затем вы создали объект, он вызвал конструктор, в конструкции у него есть метод start, который запускает поток и выполняет содержимое, написанное внутри метода run().

Так как вы создали 3 объекта (3 потока - один, два, три), все 3 потока начали выполняться одновременно.

2) Присоединение и синхронизация. Это две разные вещи. Синхронизация — это когда несколько потоков совместно используют общий ресурс, и один поток должен использовать этот ресурс одновременно. Например. Такие потоки, как DepositThread, WithdrawThread и т. д., имеют общий объект, такой как BankObject. Таким образом, пока работает DepositThread, WithdrawThread будет ждать, если они будут синхронизированы. wait(), notify(), notifyAll() используются для связи между потоками. Пожалуйста, Google, чтобы узнать больше.

о Join(), это когда работает несколько потоков, но вы присоединяетесь. например если есть два потока t1 и t2 и они работают в многопоточном окружении, вывод будет: t1-0 t2-0 t1-1 t2-1 t1-2 t2-2

и мы используем t1.join(), это будет: t1-0 t1-1 t1-2 t2-0 t2-1 t2-2

Это используется в режиме реального времени, когда иногда вы не смешиваете потоки в определенных условиях, и один зависит от завершения другого (не в общем ресурсе), поэтому вы можете вызвать метод join().

person Community    schedule 28.08.2013
comment
Хорошо, но когда t.start() встречается во время выполнения конструктора, метод run() не выполняется, вместо этого выполняется основной поток... почему это происходит?? - person user2696258; 28.08.2013
comment
Можете ли вы сказать мне, что именно вы имеете в виду основной поток? Как вы думаете, почему метод run() не выполняется? - person ; 29.08.2013
comment
Я имею в виду функцию main(), которая является текущим потоком, и я не говорю, что метод запуска вообще не запускается, я имею в виду, почему он не запускается, когда встречается t.start()... ну, я думаю, я получил его ответы в следующих сообщениях! - person user2696258; 29.08.2013

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

threadOne.join()

тогда все выполняющиеся в данный момент потоки будут приостановлены до тех пор, пока поток 1 не завершит свое выполнение или не завершится.

Рассмотрим следующий фрагмент кода:

class RunnableSample implements Runnable {
    private Thread t;
    private String threadName;

    public RunnableSample(String name) {
        this.threadName = name;
    }
    public void run() {
        try {
            for(int i = 4; i >= 1; i--) {
                System.out.println(Thread.currentThread().getName() + ", " + i);
            Thread.sleep(500);
            }
        } catch (InterruptedException e) {
            System.out.println(threadName + " interrupted");
        }
    }
    public void start() {
        if(t == null)
            t = new Thread(this, threadName);
        t.start();
        try {
            t.join();
        } catch(Exception e) {
            System.out.println(e);
        }
    }
}
public class RunnableDemo {
    public static void main(String[] args) {
        RunnableSample r1 = new RunnableSample("threadOne");
        r1.start();

        RunnableSample r2 = new RunnableSample("threadTwo");
        r2.start();

        RunnableSample r3 = new RunnableSample("threadThree");
        r3.start();
     }
}

Вывод вышеуказанной программы будет:

threadOne, 4
threadOne, 3
threadOne, 2
threadOne, 1
threadTwo, 4
threadTwo, 3
threadTwo, 2
threadTwo, 1
threadThree, 4
threadThree, 3
threadThree, 2
threadThree, 1

Поскольку join() сначала вызывается для threadOne, threadTwo и threadThree будут приостановлены до тех пор, пока threadOne не завершится. (ОТМЕТЬТЕ, что threadOne, threadTwo и ThreadThree все запущены). Теперь потоки выполняются в определенном порядке. Если в нашем примере в потоке не вызывается метод join(), то не будет порядка выполнения потоков.

public void start() {
    if(t == null)
        t = new Thread(this, threadName);
    t.start();
}

Его вывод будет:

threadOne, 4
threadThree, 4
threadTwo, 4
threadTwo, 3
threadThree, 3
threadOne, 3
threadOne, 2
threadThree, 2
threadTwo, 2
threadOne, 1
threadThree, 1
threadTwo, 1

Переходим к синхронизации, которая полезна, если вы хотите контролировать доступ нескольких потоков к какому-либо общему ресурсу. Если вы хотите ограничить доступ к общим ресурсам только одному потоку, синхронизация — лучший способ сделать это.

person Saathvik    schedule 15.06.2017

Без слов, просто работающий код

// Thread class
public class MyThread extends Thread {

    String result = null;

    public MyThread(String name) {
        super(name);
    }

    public void run() {
        for (int i = 0; i < 1000; i++) {

            System.out.println("Hello from " + this.getName());
        }
        result = "Bye from " + this.getName();
    }
}

Основной класс

public class JoinRND {
    public static void main(String[] args) {

        System.out.println("Show time");
        // Creating threads
        MyThread m1 = new MyThread("Thread M1");
        MyThread m2 = new MyThread("Thread M2");
        MyThread m3 = new MyThread("Thread M3");

        // Starting out Threads
        m1.start();
        m2.start();
        m3.start();
        // Just checking current value of thread class variable
        System.out.println("M1 before: " + m1.result);
        System.out.println("M2 before: " + m2.result);
        System.out.println("M3 before: " + m3.result);
        // After starting all threads main is performing its own logic in
        // parallel to other threads
        for (int i = 0; i < 1000; i++) {

            System.out.println("Hello from Main");
        }

        try {

            System.out
                    .println("Main is waiting for other threads to get there task completed");
            m1.join();
            m2.join();
            m3.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("M1 after" + m1.result);
        System.out.println("M2 after" + m2.result);
        System.out.println("M3 after" + m3.result);

        System.out.println("Show over");
    }
}
person Vijay Tyagi    schedule 26.04.2017

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

Thread t2 = new Thread(
             new Runnable() {
                 public void run () {
                     //do something
                 }
              }
);
Thread t1 = new Thread(
             new Runnable() {
                 public void run () {
                     //do something
                 }
              }
);
t2.start(); //Line 11
t1.start(); //Line 12
t2.join();  //Line 13
t1.join();  //Line 14
System.out.print("<Want to print something that was being modified by t2 and t1>")

Моя цель
Работают три потока, а именно t1, t2 и основной поток. Я хочу напечатать что-то после завершения t1 и t2. Операция печати находится в моем основном потоке, поэтому для ожидаемого ответа мне нужно позволить t1 и t2 завершиться, а затем распечатать мой вывод.

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

Вот определение согласно GeeksforGeeks:

Класс java.lang.Thread предоставляет метод join(), который позволяет одному потоку ждать, пока другой поток не завершит свое выполнение.

Вот один вопрос, который может развеять ваши сомнения

Q-> Будет ли поток t1 получать квант времени для выполнения планировщиком потоков, когда программа обрабатывает t2.join() в строке 13?

ANS-> Да, он будет иметь право на запуск кванта времени, поскольку мы уже сделали его приемлемым, запустив строку t1.start() в строке 11.
t2.join() применяет условие только тогда, когда JVM перейдет к следующей строке, то есть к строке 14.
Также возможно, что t1 завершит обработку в строке 13. .

person royatirek    schedule 17.09.2018

join() — это метод экземпляра класса java.lang.Thread, который мы можем использовать методом join(), чтобы гарантировать, что все потоки, начавшиеся из основного, должны заканчиваться в том же порядке, в котором они начались, а также основной должен заканчиваться последним. Другими словами, ждет, пока этот поток умрет.

Исключение: метод join() генерирует InterruptedException.

Состояние потока. Когда метод join() вызывается в потоке, он переходит из состояния выполнения в состояние ожидания. И ждать, пока тред не умрет.

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

Время ожидания: join(): ожидает завершения этого потока.

public final void join() throws InterruptedException;

Этот метод внутренне вызывает join(0). И тайм-аут 0 означает ждать вечно;

join(long миллисекунды) — синхронизированный метод. Ожидает не более миллисекунд, пока этот поток не прервется. Тайм-аут 0 означает ожидание вечно.

public final synchronized void join(long millis)
    throws InterruptedException;

public final synchronized void join(long millis, int nanos)
    throws InterruptedException;

Пример метода соединения

class MyThread implements Runnable {
     public void run() {
           String threadName = Thread.currentThread().getName();
           Printer.print("run() method of "+threadName);
           for(int i=0;i<4;i++){
                Printer.print("i="+i+" ,Thread="+threadName);
           }         
     }
}

public class TestJoin {
     public static void main(String...args) throws InterruptedException {
           Printer.print("start main()...");

           MyThread runnable = new MyThread();
           Thread thread1=new Thread(runnable);
           Thread thread2=new Thread(runnable);

           thread1.start();
           thread1.join();

           thread2.start();
           thread2.join();

           Printer.print("end main()");
     }
}

class Printer {
     public static void print(String str) {
           System.out.println(str);
     }
}

Output:
     start main()...
     run() method of Thread-0
     i=0 ,Thread=Thread-0
     i=1 ,Thread=Thread-0
     i=2 ,Thread=Thread-0
     i=3 ,Thread=Thread-0
     run() method of Thread-1
     i=0 ,Thread=Thread-1
     i=1 ,Thread=Thread-1
     i=2 ,Thread=Thread-1
     i=3 ,Thread=Thread-1
     end main()

Примечание: вызов thread1.join() заставляет основной поток ждать, пока Thread-1 не умрет.

Давайте проверим программу на использование соединения (длинные миллисекунды)

Во-первых, join(1000) будет вызываться в Thread-1, но как только 1000 миллисекунд истечет, основной поток может возобновиться и запустить thread2 (основной поток не будет ждать, пока Thread-1 умрет).

class MyThread implements Runnable {
     public void run() {
           String threadName = Thread.currentThread().getName();
           Printer.print("run() method of "+threadName);
           for(int i=0;i<4;i++){
                try {
                     Thread.sleep(500);
                } catch (InterruptedException e) {
                     e.printStackTrace();
                }
                Printer.print("i="+i+" ,Thread="+threadName);
           }         
     }
}

public class TestJoin {
     public static void main(String...args) throws InterruptedException {
           Printer.print("start main()...");

           MyThread runnable = new MyThread();
           Thread thread1=new Thread(runnable);
           Thread thread2=new Thread(runnable);

           thread1.start();

           // once 1000 millisec are up,
           // main thread can resume and start thread2.
           thread1.join(1000);

           thread2.start();
           thread2.join();

           Printer.print("end main()");
     }
}

class Printer {
     public static void print(String str) {
           System.out.println(str);
     }
}

Output:
     start main()...
     run() method of Thread-0
     i=0 ,Thread=Thread-0
     run() method of Thread-1
     i=1 ,Thread=Thread-0
     i=2 ,Thread=Thread-0
     i=0 ,Thread=Thread-1
     i=1 ,Thread=Thread-1
     i=3 ,Thread=Thread-0
     i=2 ,Thread=Thread-1
     i=3 ,Thread=Thread-1
     end main()

Для получения дополнительной информации смотрите мой блог:

http://javaexplorer03.blogspot.in/2016/05/join-method-in-java.html

person Rajesh Dixit    schedule 12.05.2016

См. Концепция очень проста.

1) Все потоки запускаются в конструкторе и, таким образом, находятся в состоянии готовности к запуску. Main уже является текущим потоком.

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

3) Теперь есть три потока, которые могут работать: t2, t3 и комбинированный поток (t1 + main).

4) Теперь, пока не завершится t1, main не может работать. поэтому выполнение двух других операторов соединения было остановлено.

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

person Ayush Jain    schedule 10.12.2018