Концепция многопоточности 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 создается, то конструктор называется where t.start() написано, но все равно run() метод не выполняется, а main() метод продолжает выполняться. Так почему это происходит?

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

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

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

12 52

12 ответов:

вы должны понимать, планирование потоков контролируется потоком scheduler.So, вы не можете гарантировать порядок выполнения потоков при нормальных обстоятельствах.

однако, вы можете использовать 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() нить).

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

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

пройти этой статьи, очень помогает.

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

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

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

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

Первое правило резьбы - "резьба-это весело"...

я не в состоянии понять поток выполнения программы, и когда ob1 создается, то конструктор вызывается where t.start() is написано, но все же 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 закончился, прежде чем пытаться получить его результат.

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

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

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

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

при создании 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). таким образом, все три потока выполняются успешно и основной поток завершается после этого

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

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

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

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

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

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

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

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

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

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

исключения: метод join () вызывает исключение InterruptedException.

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

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

время ожидания: присоединяться(): Ждет, пока эта нить умрет.

public final void join() throws InterruptedException;

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

join (long millis) – synchronized метод Ждет не более миллисекунд для этого нить, чтобы умереть. Тайм-аут 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 () сделал основной поток ждать, пока поток-1 умирает.

давайте проверим программу для использования join (long millis)

во-первых, join(1000) будет вызван на Thread-1, но как только 1000 миллисекунд вверх, основной поток может возобновить и начать thread2 (основной поток не будет ждать, пока поток-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

нет слов просто работает код

// 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");

}

}

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

threadOne.присоединяйтесь()

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

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

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 все начали). Теперь потоки выполняются в определенном порядке. Если присоединить() не вызывается в потоке, в нашем примере, то не будет порядка выполнения потоков.

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

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

часто мы хотим, чтобы основной поток к финишу последним. Но как один поток может знать, когда другой поток закончился? окончательное логическое потока isalive( ) final void join () бросает InterruptedException

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

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

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

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.
    @Override
    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 (Exception 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.");
                }
            }                       

я наткнулся на 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.присоединяйтесь() просто заставляет основной поток ждать, пока поток t1 не завершится, прежде чем перейти к следующей строке в программе.

вот определение согласно geeksforgeeks

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

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

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

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