รหัสเข้าร่วมหัวข้อนี้หมายความว่าอย่างไร


156

ในรหัสนี้ทั้งสองเข้าร่วมและทำลายหมายถึงอะไร t1.join()อะไรทำให้t2หยุดจนกว่าจะt1ยุติ?

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


3
เนื่องจากมันจะบล็อกและรอการยกเลิกเธรดทำไมคุณถึงใช้ลูป while
เมห์

@MahdiElMasaoudi ฉันคิดว่าจะรอต่อไปแม้ว่าด้ายจะถูกขัดจังหวะ? อาจไม่ใช่วิธีที่ดีในการทำเช่นนั้น
forresthopkinsa

อย่าลืมยอมรับคำตอบที่นี่หากมีประโยชน์
สีเทา

ดังที่ได้กล่าวไว้คุณไม่จำเป็นต้องwhile(true)เรียกjoinวิธีดังกล่าว
Chaklader Asfak Arefe

คำตอบ:


311

รหัสเข้าร่วมหัวข้อนี้หมายความว่าอย่างไร

ในการอ้างอิงจากThread.join()เมธอด javadocs :

join() รอให้เธรดนี้ตาย

มีด้ายที่ใช้รหัสตัวอย่างของคุณซึ่งอาจเป็นหัวข้อหลัก

  1. เธรดหลักสร้างและเริ่มต้นt1และt2เธรด เธรดทั้งสองเริ่มต้นทำงานแบบขนาน
  2. เธรดหลักเรียกt1.join()เพื่อรอt1เธรดให้เสร็จ
  3. t1เสร็จสมบูรณ์ด้ายและt1.join()วิธีการส่งกลับในหัวข้อหลัก โปรดทราบว่าt1อาจเสร็จสิ้นไปแล้วก่อนที่จะjoin()โทรออกในกรณีนี้การjoin()โทรจะกลับมาทันที
  4. เธรดหลักเรียกt2.join()เพื่อรอt2เธรดให้เสร็จ
  5. t2เสร็จสมบูรณ์ด้าย (หรือมันอาจจะเสร็จสิ้นก่อนที่จะt1ด้ายได้) และt2.join()วิธีการส่งกลับในหัวข้อหลัก

สิ่งสำคัญคือต้องเข้าใจว่าt1และt2เธรดได้ทำงานแบบขนานแต่เธรดหลักที่เริ่มต้นต้องรอให้เสร็จก่อนจึงจะสามารถดำเนินการต่อได้ นั่นเป็นรูปแบบทั่วไป นอกจากนี้t1และ / หรือt2อาจเสร็จสิ้นก่อนที่เธรดหลักจะเรียกjoin()ใช้ ถ้าเป็นเช่นนั้นjoin()จะไม่รอ แต่จะกลับมาทันที

t1.join() หมายถึงทำให้ t2 หยุดจนกว่า t1 จะสิ้นสุดลง?

ไม่ได้เธรดหลักที่กำลังเรียกt1.join()จะหยุดการทำงานและรอให้t1เธรดเสร็จสิ้น t2ด้ายทำงานในแบบขนานและไม่ได้รับผลกระทบจากt1หรือt1.join()โทรที่ทั้งหมด

ในแง่ของการลอง / จับการjoin()โยนInterruptedExceptionหมายถึงเธรดหลักที่กำลังเรียกjoin()อาจถูกขัดจังหวะโดยเธรดอื่น

while (true) {

มีตัวเชื่อมในการwhileวนซ้ำเป็นลวดลายแปลก ๆ โดยทั่วไปคุณจะเข้าร่วมครั้งแรกจากนั้นเข้าร่วมครั้งที่สองจัดการInterruptedExceptionอย่างเหมาะสมในแต่ละกรณี ไม่จำเป็นต้องใส่ไว้ในลูป


24
+1 นั่นเป็นรูปแบบที่แปลกมากและอาจถูกลบออกได้
m0skit0

3
ถ้า t1 เสร็จสิ้นจากนั้น t2 เพื่อเสร็จสิ้น ดูเหมือนว่าจะเป็นกระบวนการต่อเนื่อง หนึ่งเธรดเสร็จสิ้นก่อนจากนั้นเธรดอื่น ๆ เธรดหลายเธรดคืออะไร
user697911

9
เพราะt1และt2สามารถทำงานในแบบคู่ขนาน เป็นเพียงmainความต้องการที่พวกเขาทั้งคู่ต้องทำให้เสร็จก่อนที่จะสามารถดำเนินการต่อ นั่นเป็นรูปแบบทั่วไป @ user697911
สีเทา

3
มีwhileลูปอยู่ที่นั่นเพราะ (ฉันเดา) ต้องการลองjoin()โทรอีกครั้งหากมีการขัดจังหวะหรือไม่ ฉันจะไม่เขียนอย่างนั้นอย่างแน่นอน @ user697911
สีเทา

5
มีลูปอยู่ที่นั่นเพื่อให้แน่ใจว่าทั้งสองt1และt2เสร็จสิ้น กล่าวคือ ถ้าt1พ่นก็จะกลับห่วงและคอยInterruptedException t2อีกทางเลือกหนึ่งคือรอให้ทั้งสองเธรดในแต่ละ Try-Catch เพื่อหลีกเลี่ยงการวนซ้ำ นอกจากนี้ขึ้นอยู่กับEventThreadว่าสามารถทำได้ด้วยวิธีนี้เนื่องจากเราใช้ 2 เธรดไม่ใช่หนึ่งเธรด
Michael Bisbjerg

68

นี่คือคำถามสัมภาษณ์ Java ที่ชื่นชอบ

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

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

t1.join() หมายความว่า t1 พูดอะไรบางอย่างเช่น "ฉันต้องการให้เสร็จก่อน " t2เป็นกรณีเดียวกันกับ ไม่ว่าใครจะเริ่มต้นt1หรือt2ด้าย (ในกรณีนี้mainวิธีการ) หลักจะรอจนกว่าt1และt2เสร็จสิ้นการงานของพวกเขา

แต่จุดสำคัญที่จะต้องทราบลงt1และt2ตัวเองสามารถทำงานในแบบคู่ขนานโดยไม่คำนึงถึงลำดับเข้าร่วมการโทร บนและt1 t2มันเป็นmain/daemonหัวข้อที่มีรอ


3
ตัวอย่างที่ดี เกี่ยวกับ "สามารถทำงานแบบขนาน": แล้วอะไรนะ? เป็นสิ่งสำคัญที่เธรดหลักจะรอเป็นครั้งแรกสำหรับ t1 และจากนั้นสำหรับ t2 จริงๆมันไม่สำคัญว่าสิ่งที่ T1 หรือ T2 กำลังทำ (จากมุมมองของหัวข้อหลัก)
อเล็กซ์

1
คุณพูดถึงเธรด "main / daemon" หลักที่ฉันเข้าใจ แต่ภูตไม่มีอะไรเกี่ยวข้องกับมัน เธรดหลักคือ non-daemon
สีเทา

1
t1.join(); t2.join();จะไม่อนุญาตให้เธรดที่ดำเนินการรวมเพื่อดำเนินการต่อจนกว่ากระทู้ทั้งสองจะยุติ ในกรณีที่ไม่มีรหัสผิดปกติมาก ๆ ลำดับของการรวมไม่สำคัญ
David Schwartz

ดังนั้นจะ t2.join () จะถูกเรียกเฉพาะเมื่อ t1 เสร็จแล้ว?
Leo Droidcoder

กล่าวอีกนัยหนึ่งถ้าเราต้องการ "ดำเนินการ" ต่อเนื่องของเธรด t1 และ t2 เราต้องวาง t1.join () ทันทีหลังจาก t1.start () ตั้งแต่เธรดหลักเริ่ม t1 (และ t2 หลังจากนั้น) และแน่นอนลบออกจาก ลอง / จับ เห็นได้ชัดว่าการทำเช่นนั้นผลที่ตามมาจะสูญเสียความเท่าเทียม
dobrivoje

47

join()หมายถึงการรอเธรดให้เสร็จสมบูรณ์ นี่เป็นวิธีการบล็อก หัวข้อหลักของคุณ (หนึ่งที่ไม่ได้join()) จะรอในt1.join()บรรทัดจนเสร็จสิ้นการทำงานของมันแล้วจะทำเช่นเดียวกันสำหรับt1t2.join()


29

ภาพที่มีค่าพันคำ.

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

หวังว่าจะเป็นประโยชน์สำหรับรายละเอียดเพิ่มเติมคลิกที่นี่


3
ชัดเจนและแม่นยำ
dobrivoje

10

เมื่อเธรด tA เรียกใช้ tB.join () สาเหตุของมันไม่เพียง แต่รอให้ tB ตายหรือ tA ถูกขัดจังหวะ แต่สร้างความสัมพันธ์ก่อนเกิดขึ้นระหว่างคำสั่งสุดท้ายใน tB และคำสั่งถัดไปหลังจาก tB.join () ในเธรด tA

การดำเนินการทั้งหมดในเธรดเกิดขึ้นก่อนที่เธรดอื่นจะส่งคืนสำเร็จจากการเข้าร่วม () ในเธรดนั้น

มันหมายถึงโปรแกรม

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

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

พิมพ์เสมอ

>> 1111111111111111111111111 ...

แต่โปรแกรม

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

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

สามารถพิมพ์ได้ไม่เพียง

>> 0000000000 ... 000000111111111111111111111111 ...

แต่

>> 00000000000000000000000000000000000000000000 ... 

เสมอ '0'

เนื่องจาก Java Memory Model ไม่ต้องการ 'ถ่ายโอน' ค่าใหม่ของ 'sharedVar' จาก threadB ไปยังเธรดหลักโดยไม่มี heppens - ก่อนความสัมพันธ์ (การเริ่มต้นเธรดการรวมเธรดการใช้คำสำคัญ 'ซิงโครไนซ์' การใช้ตัวแปร AtomicXXX เป็นต้น)


5

ใส่เพียง:
t1.join()ผลตอบแทนหลังจากt1เสร็จสิ้น
มันไม่ได้ทำอะไรเลยที่จะด้ายt1ยกเว้นรอให้เสร็จ
โดยปกติแล้วโค้ดต่อไปนี้ t1.join()จะถูกเรียกใช้งานหลังจาก t1.join()ส่งคืนเท่านั้น


1
จะถูกดำเนินการหลังจาก t1.join () ส่งคืน +1
mohsen.nour

3

จากหน้าเอกสาร oracle บน Joins

joinวิธีการช่วยให้หนึ่งในหัวข้อที่จะรอให้เสร็จสิ้นของผู้อื่น

ถ้า t1 เป็นThreadวัตถุที่เธรดกำลังทำงานอยู่

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

ถ้า t2 เป็นThreadวัตถุที่เธรดกำลังทำงานอยู่

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

joinAPI เป็น API ระดับต่ำซึ่งได้รับการแนะนำใน java เวอร์ชันก่อนหน้า มีการเปลี่ยนแปลงหลายสิ่งในช่วงเวลาหนึ่ง (โดยเฉพาะอย่างยิ่งกับการเปิดตัว jdk 1.5) ที่เกิดขึ้นพร้อมกัน

คุณสามารถทำเช่นเดียวกันกับ java.util.concurrent API ตัวอย่างบางส่วน ได้แก่

  1. ใช้invokeAllเปิดExecutorService
  2. ใช้CountDownLatch
  3. ใช้ForkJoinPoolหรือnewWorkStealingPoolของExecutors(ตั้งแต่ java 8)

อ้างถึงคำถาม SE ที่เกี่ยวข้อง:

รอจนกว่าเธรดทั้งหมดจะเสร็จสิ้นการทำงานใน java


1

สำหรับฉันพฤติกรรม Join () มักสับสนเพราะฉันพยายามจดจำว่าใครจะรอใคร อย่าพยายามจำอย่างนั้น

ภายใต้เป็นกลไกการรอ () และการแจ้งเตือน () ที่บริสุทธิ์

เราทุกคนรู้ว่าเมื่อเราเรียก wait () บนวัตถุใด ๆ (t1) การเรียก object (main) จะถูกส่งไปยังห้องรอ (สถานะถูกบล็อค)

ที่นี่หัวข้อหลักกำลังเรียกเข้าร่วม () ซึ่งรอ () ภายใต้ฝาครอบ ดังนั้นเธรดหลักจะรอจนกว่าจะได้รับแจ้ง การแจ้งเตือนถูกกำหนดโดย t1 เมื่อเสร็จสิ้นการรัน (การเสร็จสิ้นเธรด)

หลังจากได้รับการแจ้งเตือนหลักจะออกจากห้องรอและดำเนินการต่อ


0

หวังว่ามันจะช่วย!

package join;

public class ThreadJoinApp {

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

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

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

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

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

    //Happy coding -- Parthasarathy S
}

-3

สมมติว่าเธรดหลักของเราเริ่มเธรด t1 และ t2 ตอนนี้เมื่อมีการเรียก t1.join () เธรดหลักจะหยุดตัวเองจนกว่าเธรด t1 จะตายจากนั้นกลับมาทำงานต่อ ในทำนองเดียวกันเมื่อ t2.join () ดำเนินการเธรดหลักจะหยุดตัวเองอีกครั้งจนกว่าเธรด t2 จะตายจากนั้นกลับมาทำงานต่อ

ดังนั้นนี่คือวิธีการทำงาน

ยิ่งไปกว่านั้นในขณะที่ไม่จำเป็นต้องวนรอบที่นี่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.