web-dev-qa-db-ja.com

Javaマルチスレッドの概念とjoin()メソッド

Javaのスレッドで使用されるjoin()メソッドで混乱しています。次のコードでは:

// 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の使用は何ですか?

ここで基本的な概念が欠けていることは知っていますが、それを理解することはできませんので、助けてください。

56
user2696258

スレッドのスケジューリングはスレッドスケジューラによって制御されるため、通常の状況でのスレッドの実行順序を保証することはできません。

ただし、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ブロック内にある必要があります。これは、throwsInterruptedException例外であるためです。そうしないと、コンパイル時にエラーが発生します。したがって、次のようになります。

try{
    t.join();
}catch(InterruptedException e){
    // ...
}
159
Malwaregeek

まず、ob1を作成すると、コンストラクターが呼び出され、実行が開始されます。そのとき、t.start()も別のスレッドで実行されます。新しいスレッドが作成されると、メインスレッドと並行して実行されることに注意してください。そして、それがメインが次のステートメントで再び実行を開始する理由です。

Join()ステートメントを使用して、子スレッドが孤立するのを防ぎます。メインクラスでjoin()を呼び出さなかった場合、メインスレッドは終了します実行後、子スレッドはステートメントを実行し続けます。 Join()は、すべての子スレッドの実行が完了するまで待機し、メインメソッドのみが終了します。

this の記事を読み進めてください。

5
Vimal Bera

プログラムの実行の流れを理解できません。ob1が作成されると、t.start()が書き込まれたコンストラクターが呼び出されますが、run()メソッドは実行されず、main()メソッドが実行を継続します。なぜこれが起こっているのですか?

メインは同じ優先順位を共有するため、これはスレッドスケジューラに依存します。 start()を呼び出すことは、run()がすぐに呼び出されることを意味するものではなく、スレッドの実行を選択するときにスレッドスケジューラに依存します。

join()メソッドは、呼び出されたスレッドが終了しないまで待機するために使用されますが、ここの出力では、なぜスレッドの代替出力が表示されますか?

これは、コード内のThread.sleep(1000)が原因です。その行を削除すると、ob1がob2の前に終了し、ob2がob3の前に終了します(join()で予想されるとおり)。それはすべて、ob1、ob2、ob3がいつ開始されたかに依存すると言いました。 sleepを呼び出すと、スレッドの実行が1秒以上(コード内)一時停止し、スケジューラが待機中の他のスレッドを呼び出す機会を与えます(同じ優先順位)。

3
rmishra

スレッド化の最初のルール-「スレッド化は楽しい」...

プログラムの実行フローを理解できません。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()メソッドは、呼び出されたスレッドが終了しないまで待機するために使用されますが、ここでの出力では、スレッドの代替出力が表示されます。なぜですか?

質問が間違っていると言った方法は...joinThreadを待ってから戻る前に死ぬように要求されます。たとえば、Threadの結果に依存している場合、joinを使用して、結果を取得する前にThreadがいつ終了したかを知ることができます。

同様に、スレッドをポーリングすることもできますが、これは代わりにThreadが使用できるCPUサイクルを消費します...

1
MadProgrammer

競合状態について学んでいる間に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>")

私のAIM
3つのスレッド、つまりt1、t2、およびメインスレッドが実行されています。 t1とt2が終了した後に何かを印刷したい。印刷操作はメインスレッドで行われるため、予想される答えを得るには、t1とt2を終了させて​​から出力を印刷する必要があります。

したがって、t1.join()は、プログラムの次の行に進む前にt1スレッドが完了するまで、メインスレッドを待機させるだけです。

GeeksforGeeks による定義は次のとおりです。

Java.lang.Threadクラスは、1つのスレッドが別のスレッドの実行を完了するまで待機できるようにするjoin()メソッドを提供します。

ここにあなたの疑問を解決するかもしれない1つの質問があります

Q->t1スレッドは、プログラムが13行目

ANS->はい、すでにt1.start()行11行目.
t2.join()は、JVMが次の行、つまり14行目に移動するときにのみ条件を適用します。
t1が行13で処理を終了する可能性もあります。

1
royatirek

JVMおよび基盤となるOSは、物事をスケジュールするときにかなりの自由があります。個々のスレッドからの出力が表示される前に「スレッドの終了を待機する」まで到達するという事実は、単にスレッドの起動に少し時間がかかることを意味する場合があります(つまり、スレッドが「そして、run()メソッドが実際に実行を開始したとき)。スレッド出力がより早く表示される可能性がありますが、どちらの方法でも保証されません。

join()に関しては、参加しているスレッドが完了した後にのみ、それ以降が発生することを保証するだけです。したがって、3つのjoin()呼び出しが連続してある場合、スレッドが特定の順序で終了する必要があるわけではありません。それは単にob1を最初に待つことを意味します。 ob1が終了すると、ob2およびobはまだ実行中か、すでに終了している可能性があります。それらが終了すると、他のjoin()呼び出しはすぐに戻ります。

synchronizedは、複数のスレッドが同じオブジェクトにアクセスして変更を加える場合に特に使用されます。同期ブロックは、2つのスレッドによって同時に実行されることは決してありません。つまり、それを実行するスレッドは、それ自体に同期オブジェクトを持っています。

1
codingjourney

ob1が作成されると、コンストラクタが呼び出され、「t.start()」が書き込まれますが、run()メソッドは実行されず、main()メソッドがさらに実行されます。なぜこれが起こっているのですか?

ここで、スレッドとメインスレッドの優先度は同じです。同じ優先度のスレッドの実行は、Thread schedularに完全に依存します。どちらを最初に実行するかは予想できません。

join()メソッドは、呼び出されたスレッドが終了しないまで待機するために使用されますが、ここの出力では、なぜスレッドの代替出力が表示されますか?

ここで、メインスレッドから以下のステートメントを呼び出します。

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

したがって、メインスレッドはob1.tob2.tob3.tスレッドが死ぬまで待機します( Thread#join doc を調べます)。したがって、3つのスレッドはすべて正常に実行され、メインスレッドその後完了する

1
Prabhaker

スレッドスケジューラは、スレッドのスケジューリングを担当します。したがって、プログラムを実行するたびに、スレッドの実行順序が保証されるわけではありません。 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

同期に来ます。これは、共有リソース上の複数のスレッドのアクセスを制御する場合に便利です。共有リソースにアクセスするスレッドを1つだけに制限する場合は、同期が最適な方法です。

1
Saathvik

コードを実行するだけの言葉はありません

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

私のコメント:

出力を見ると、出力はスレッド名であるOne、Two、Threeと混合され、同時に実行されます。スレッドがmainメソッドで実行されていないと言うときはわかりません。

あなたの質問を理解したかどうかわからない。しかし、私が理解できることを答えて、それがあなたを助けることを願っています。

1)次に、コンストラクターと呼ばれるオブジェクトを作成しました。これには、スレッドを開始し、run()メソッド内に記述されたコンテンツを実行するstartメソッドが構築されています。

したがって、3つのオブジェクト(3つのスレッド-1つ、2つ、3つ)を作成すると、3つのスレッドすべてが同時に実行を開始しました。

2)結合と同期これらは2つの異なるものです。同期とは、共通リソースを共有する複数のスレッドがあり、1つのスレッドがそのリソースを一度に使用する場合です。例えば。 DepositThread、WithdrawThreadなどのスレッドは、BankObjectとして共通のオブジェクトを共有します。そのため、DepositThreadの実行中に、WithdrawThreadは同期されると待機します。 wait()、notify()、notifyAll()はスレッド間通信に使用されます。 Plz googleで詳細を確認します。

join()については、複数のスレッドが実行中ですが、参加します。例えば2つのスレッド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()メソッドを呼び出すことができます。

1
user1460153

join()はJava.lang.Threadクラスのインスタンスメソッドであり、join()メソッドを使用して、mainから開始したすべてのスレッドを開始順に終了し、mainも最後に終了するようにすることができます。つまり、このスレッドが死ぬのを待ちます。

Exception:join()メソッドはInterruptedExceptionをスローします。

スレッド状態:スレッドでjoin()メソッドが呼び出されると、実行状態から待機状態になります。そして、スレッドが死ぬのを待ちます。

同期ブロック:スレッドは、join()メソッドを呼び出す前にオブジェクトロックを取得する必要はありません。すなわち、join()メソッドは外部の同期ブロックから呼び出すことができます。

待機時間: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()を呼び出すと、スレッド1が終了するまでメインスレッドが待機するようになります。

Joinを使用するプログラムをチェックしましょう(長いミリ秒)

最初に、スレッド1でjoin(1000)が呼び出されますが、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

0
Rajesh Dixit

コンセプトは非常にシンプルです。

1)すべてのスレッドはコンストラクターで開始されるため、実行可能な状態になります。 Mainはすでに実行中のスレッドです。

2)ここで、t1.join()を呼び出しました。ここで起こるのは、メインスレッドがt1スレッドの後ろに結び付けられることです。したがって、t1の下端にmainが接続された長いスレッドを想像できます。

3)現在、実行できるスレッドは3つあります:t2、t3、結合スレッド(t1 +メイン)。

4)現在、t1が終了するまでmainは実行できません。そのため、他の2つの結合ステートメントの実行は停止されています。

5)したがって、スケジューラーは、上記の(ポイント3で)スレッドのどれを実行するかを決定し、出力を説明します。

0
ayush jain