Chủ đề này tham gia mã có nghĩa là gì?


156

Trong mã này, hai tham gia và phá vỡ có nghĩa là gì? t1.join()nguyên nhân t2dừng lại cho đến khi t1chấm dứt?

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
Vì nó sẽ chặn và chờ kết thúc luồng, tại sao bạn lại sử dụng vòng lặp while?
Mehdi

@MahdiElMasaoudi Tôi cho rằng, để tiếp tục chờ đợi ngay cả khi chủ đề bị gián đoạn? Có lẽ không phải là một cách tuyệt vời để làm điều đó
forresthopkinsa

Hãy nhớ chấp nhận một câu trả lời ở đây nếu nó hữu ích.
Xám

Như đã đề cập, bạn không cần phải while(true)gọi joinphương thức.
Chaklader Asfak Arefe

Câu trả lời:


311

Chủ đề này tham gia mã có nghĩa là gì?

Để trích dẫn từ Thread.join()phương thức javadocs :

join() Chờ đợi chủ đề này để chết.

Có một luồng đang chạy mã ví dụ của bạn có lẽ là luồng chính .

  1. Các chủ đề chính tạo và bắt đầu t1t2 chủ đề. Hai luồng bắt đầu chạy song song.
  2. Các luồng chính gọi t1.join()để chờt1 chủ đề kết thúc.
  3. Chuỗi t1hoàn thành và t1.join()phương thức trả về trong luồng chính. Lưu ý rằng t1có thể đã kết thúc trước khi join()cuộc gọi được thực hiện trong trường hợp đójoin() cuộc gọi sẽ trở lại ngay lập tức.
  4. Các luồng chính gọi t2.join()để chờt2 chủ đề kết thúc.
  5. Chuỗi t2hoàn thành (hoặc nó có thể đã hoàn thành trước khi t1luồng thực hiện) và t2.join()phương thức trả về trong luồng chính.

Điều quan trọng là phải hiểu rằng các luồng t1t2luồng đã chạy song song nhưng luồng chính bắt đầu chúng cần đợi chúng kết thúc trước khi nó có thể tiếp tục. Đó là một mô hình phổ biến. Ngoài ra, t1và / hoặc t2có thể đã kết thúc trước khi luồng chính gọi join()chúng. Nếu vậy thì join()sẽ không chờ mà sẽ quay lại ngay.

t1.join() nghĩa là khiến t2 dừng lại cho đến khi t1 chấm dứt?

Không. Chuỗi chính đang gọi t1.join()sẽ ngừng chạy và đợi t1luồng kết thúc. Chuỗi t2đang chạy song song và không bị ảnh hưởng bởi t1hoặc t1.join()cuộc gọi nào cả.

Về mặt thử / bắt, các join()cú ném InterruptedExceptioncó nghĩa là luồng chính đang gọi join()có thể bị gián đoạn bởi một luồng khác.

while (true) {

Có các tham gia trong một whilevòng lặp là một mô hình kỳ lạ. Thông thường, bạn sẽ thực hiện tham gia đầu tiên và sau đó tham gia thứ hai xử lý InterruptedExceptionthích hợp trong từng trường hợp. Không cần phải đặt chúng trong một vòng lặp.


24
+1 Đó là một mẫu rất lạ và có thể được loại bỏ.
m0skit0

3
Nếu t1 kết thúc trước, thì t2 kết thúc. Đó dường như là một quá trình tuần tự. Một chủ đề kết thúc đầu tiên, sau đó khác. điểm của nhiều luồng là gì?
dùng697911

9
Bởi vì t1t2có thể chạy song song. Chỉ là mainnhu cầu cả hai phải hoàn thành trước khi nó có thể tiếp tục. Đó là một mẫu điển hình @ user697911.
Xám

3
Các whilevòng lặp là có vì (tôi đoán) mà nó muốn thử lại các join()cuộc gọi nếu một bị gián đoạn? Tôi chắc chắn sẽ không viết nó theo cách đó @ user697911.
Xám

5
Vòng lặp ở đó để đảm bảo rằng cả hai t1t2kết thúc. I E. nếu t1ném InterruptedException, nó sẽ lặp lại và chờ đợi t2. Một cách khác là chờ cả hai luồng trong mỗi lần thử của chúng, vì vậy vòng lặp có thể tránh được. Ngoài ra, tùy thuộc vào EventThread, có thể có ý nghĩa để làm theo cách này, vì chúng tôi đang chạy 2 luồng chứ không phải một.
Michael Bisbjerg

68

Đây là một câu hỏi phỏng vấn Java yêu thích .

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()có nghĩa là, t1 nói điều gì đó như " Tôi muốn hoàn thành trước ". Tương tự là trường hợp với t2. Bất kể ai bắt đầu t1hoặc t2luồng (trong trường hợp này là mainphương thức), main sẽ đợi cho đến khi t1t2 hoàn thành nhiệm vụ của họ.

Tuy nhiên, một điểm quan trọng cần lưu ý t1t2bản thân chúng có thể chạy song song bất kể chuỗi cuộc gọi tham gia trên t1t2. Đó là main/daemonchủ đề phải chờ đợi .


3
Ví dụ tốt. Về "có thể chạy song song": Vậy thì sao? Điều quan trọng là luồng chính sẽ đợi FIRST cho t1 và THEN cho t2. Nó thực sự không quan trọng việc t1 hay t2 đang làm gì (từ phối cảnh chủ đề chính)
Alex

1
Bạn đề cập đến chủ đề "chính / daemon". Chính tôi hiểu nhưng daemon không có gì để làm với nó. Chủ đề chính là không daemon.
Xám

1
t1.join(); t2.join();sẽ không cho phép luồng thực thi các phép nối tiếp tục cho đến khi cả hai luồng kết thúc. Trong trường hợp không có mã rất bất thường ở nơi khác, thứ tự của các phép nối không thành vấn đề.
David Schwartz

Vì vậy, t2.join () sẽ chỉ được gọi khi t1 kết thúc?
Leo Droidcoder

Nói cách khác, nếu chúng ta muốn "tuần tự hóa" việc thực thi các luồng t1 và t2, chúng ta cần đặt t1.join () ngay sau t1.start () vì luồng chính bắt đầu t1 (và t2 sau đó) và tất nhiên loại bỏ nó khỏi cố gắng bắt. Rõ ràng, làm như vậy, hậu quả sẽ là mất song song.
dobrivoje

47

join()có nghĩa là chờ đợi một chủ đề để hoàn thành. Đây là một phương pháp chặn. Chủ đề chính của bạn (một trong đó thực hiện join()) sẽ chờ trên t1.join()dòng cho đến khi t1hoàn thành công việc của nó, và sau đó sẽ làm tương tự cho t2.join().


29

Một bưc tranh đang gia ngan lơi noi.

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

Hy vọng sẽ hữu ích, để biết thêm chi tiết bấm vào đây


3
Rõ ràng và chính xác.
dobrivoje

10

Khi luồng tA gọi tB.join (), các nguyên nhân của nó không chỉ chờ tB chết hoặc tA bị gián đoạn mà còn tạo ra mối quan hệ xảy ra trước câu lệnh cuối trong tB và câu lệnh tiếp theo sau tB.join () trong luồng tA.

Tất cả các hành động trong một luồng xảy ra - trước khi bất kỳ luồng nào khác trả về thành công từ một phép nối () trên luồng đó.

Nó có nghĩa là chương trình

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

Luôn in

>> 1111111111111111111111111 ...

Nhưng chương trình

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

Có thể in không chỉ

>> 0000000000 ... 000000111111111111111111111111 ...

Nhưng

>> 00000000000000000000000000000000000000000000 ... 

Luôn luôn chỉ '0'.

Bởi vì Mô hình bộ nhớ Java không yêu cầu 'chuyển' giá trị mới của 'sharedVar' từ luồngB sang luồng chính mà không có mối quan hệ trước gan (bắt đầu luồng, nối chuỗi, sử dụng từ khóa 'đồng bộ hóa', sử dụng biến AtomicXXX, v.v.).


5

Đơn giản chỉ cần đặt:
t1.join()trả về sau khi t1hoàn thành.
Nó không làm gì để xâu chuỗi t1, ngoại trừ đợi nó kết thúc.
Đương nhiên, mã sau t1.join()sẽ được thực hiện chỉ sau khi t1.join()trả về.


1
sẽ chỉ được thực hiện sau khi t1.join () trả về +1
mohsen.nour

3

Từ trang tài liệu oracle trên Joins

Các joinphương pháp cho phép một thread để chờ đợi để hoàn của người khác.

Nếu t1 là một Threadđối tượng có luồng hiện đang thực thi,

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

Nếu t2 là một Threadđối tượng có luồng hiện đang thực thi,

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

joinAPI là API cấp thấp, đã được giới thiệu trong các phiên bản trước của java. Rất nhiều thứ đã được thay đổi trong một khoảng thời gian (đặc biệt là với bản phát hành jdk 1.5) trên mặt trận đồng thời.

Bạn có thể đạt được điều tương tự với java.util.conc hiện API. Một số ví dụ là

  1. Sử dụng invoke ALL trênExecutorService
  2. Sử dụng CountDownLatch
  3. Sử dụng ForkJoinPool hoặc newWorkStealsPool của Executors(kể từ java 8)

Tham khảo các câu hỏi SE liên quan:

chờ cho đến khi tất cả các chủ đề kết thúc công việc của họ trong java


1

Đối với tôi, hành vi Tham gia () luôn khó hiểu vì tôi đang cố nhớ ai sẽ đợi ai. Đừng cố nhớ nó theo cách đó.

Bên dưới, nó là cơ chế Wait () và notify () thuần túy.

Chúng ta đều biết rằng, khi chúng ta gọi Wait () trên bất kỳ đối tượng nào (t1), đối tượng gọi (chính) sẽ được gửi đến phòng chờ (Trạng thái bị chặn).

Ở đây, chủ đề chính đang gọi tham gia () đang chờ () dưới vỏ bọc. Vì vậy, chủ đề chính sẽ chờ cho đến khi nó được thông báo. Thông báo được đưa ra bởi t1 khi hoàn thành chạy (hoàn thành luồng).

Sau khi nhận được thông báo, main ra khỏi phòng chờ và tiến hành thực hiện.


0

Hy vọng nó giúp!

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

giả sử chủ đề chính của chúng ta bắt đầu các chủ đề t1 và t2. Bây giờ, khi t1.join () được gọi, luồng chính sẽ tự treo cho đến khi luồng t1 chết và sau đó tự phục hồi. Tương tự, khi t2.join () thực thi, luồng chính sẽ tự treo một lần nữa cho đến khi luồng t2 chết và sau đó tiếp tục lại.

Vì vậy, đây là cách nó hoạt động.

Ngoài ra, vòng lặp while không thực sự cần thiết ở đây.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.