Подтвердить что ты не робот

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

Я запутался в методе join(), используемом в Threads в 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

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

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

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

И если использование join заключается в том, что используется synchronized??

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

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

Пройдите эту статью, которая помогает много.

Ответ 3

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

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

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

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

Ответ 4

Первое правило Threading - "Threading is fun"...

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

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

Ответ 5

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

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

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

Ответ 6

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

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

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

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

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

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

Ответ 7

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

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

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

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

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

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

about Join(), когда выполняется несколько потоков, но вы присоединяетесь. например если есть два потока t1 и t2 и в многопоточном env они запускаются, выход будет: 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().

Ответ 8

Часто мы хотим, чтобы основной поток заканчивался последним. Но как один поток может знать, когда закончился другой поток? final boolean 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.");
                }
            }                       

Ответ 9

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

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

Ответ 10

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

threadOne.join()

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

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

Ответ 11

Я наткнулся на 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.

Ответ 12

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

Исключение: Метод join() выбрасывает InterruptedException.

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

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

Время ожидания: присоединиться(): Ожидает, что эта нить умрет.

public final void join() throws InterruptedException;

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

join (long millis) - синхронизированный метод Ожидает больше миллиса миллисекунд, чтобы эта нить умереть. Тайм-аут 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 не умрет.

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

Во-первых, 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

Ответ 13

Видеть концепцию очень просто.

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

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

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

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

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