5_关于Thread的join方法

本文深入解析Java中Thread类的join方法,包括其三种重载形式及作用,通过示例展示如何使用join方法控制线程间的执行顺序,帮助理解线程同步机制。
  • 在Thread类中一共有__3种__join函数的重载形式,都是非静态函数

      public final void join() throws InterruptedException;
    
      public final synchronized void join(long millis) throws InterruptedException
    
      public final synchronized void join(long millis, int nanos) throws InterruptedException
    

    (其实它们仨的根源函数都是第二种形式)

  • 第二种形式的源码

      public final synchronized void join(long millis) throws InterruptedException {
          
          long base = System.currentTimeMillis();
          long now = 0;
    
          if (millis < 0) {
              throw new IllegalArgumentException("timeout value is negative");
          }
    
          if (millis == 0) {
              while (isAlive()) {
                  wait(0);            //持有的是被等待线程t的锁, 那么被什么notify()或interrupt呢?
              }
          } else {
              while (isAlive()) {
                  long delay = millis - now;
                  if (delay <= 0) {
                      break;
                  }
                  wait(delay);
                  now = System.currentTimeMillis() - base;
              }
          }
      }
    
  • join函数的__作用__是:调用join函数的线程会进入等待状态(它等待的正是使用了join方法的线程实例,例如 在主线程中调用eatThread.join(), 那么主线程就会等待eatThread结束,才能再次重新运行状态或就绪状态)

  • 带参数的join方法,代表当前线程只等待另一个线程 xx毫秒,然后它就又恢复到运行状态或就绪状态了;

    不带参数的join方法,代表当前线程只能一直等,知道另一个线程完全结束才能恢复到运行状态或就绪状态

  • 示例

      class MyRunnable implements Runnable{
    
          @Override
          public void run() {
    
              System.out.println("Thread started::"+Thread.currentThread().getName());
    
              try {
                  Thread.sleep(40000);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
    
              System.out.println("Thread ended::"+Thread.currentThread().getName());
          }
      }
    
    
      class ThreadJoinExample {
    
          public static void main(String[] args) {
    
              Thread t1 = new Thread(new MyRunnable(), "t1");
              Thread t2 = new Thread(new MyRunnable(), "t2");
              Thread t3 = new Thread(new MyRunnable(), "t3");
    
              t1.start();
    
              //start second thread after waiting for 20 seconds or if it's dead
              try {
                  t1.join(20000);
    
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
    
              t2.start();
    
              //start third thread only when first thread is dead
              try {
                  t1.join();
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
    
              t3.start();
    
              //let all threads finish execution before finishing main thread
              try {
                  t1.join();
                  t2.join();
                  t3.join();
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
    
              System.out.println("All threads are dead, exiting main thread");
          }
      }
    

    这个示例的结果是

      Thread started::t1  ---间隔大概20秒-->
      Thread started::t2  ---间隔大概20秒-->
      Thread ended::t1    ---几乎无间隔-->
      Thread started::t3  ---间隔大概20秒-->
      Thread ended::t2    ---间隔大概20秒-->
      Thread ended::t3    ---几乎无间隔-->
      All threads are dead, exiting main thread
    

    说明

    1° 在执行 t1.start()之前,程序只有一个主线程

    2° 执行 t1.start()后,很快 t1线程开始,输出Thread started::t1,然后开始长达40秒的sleep

    3° t1线程开始sleep以后,主线程继续向下执行,执行到t1.join(20000)时,代表主线程必须要等待20秒后或t1线程结束后,才能继续向下执行

    4° 20秒过去了,t1线程没有结束,此时它已经睡了20秒,主线程的等待时间过去了,它可以继续向下执行 t2.start(),很快t2线程开始,输出Thread started::t2,然后也开始长达40秒的sleep

    5° 现在t1和t2都在sleep,因此又会执行主线程,执行到t1.join()时,代表主线程只能等待t1结束才能向下执行

    6° 由于在4°时t1线程已经睡了20秒,因此它又睡了20秒后,会输出Thread ended::t1,线程t1结束

    7° 线程t1结束使得主线程可以继续向下执行,执行到t3.start()时,很快t3线程开始,输出Thread started::t3,然后也开始长达40秒的sleep

    8° 现在的情况是,线程t2还要再睡20秒(因为有20秒的时间是和t1一起睡的),线程t3还要睡40秒,因此又到了主线程,主线程向下执行t1.join(),但由于t1线程已经结束,所以继续向下执行;主线程执行t2.join(),此时要等t2结束才能向下执行

    9° 又过了大概20秒,t2线程睡醒,输出Thread ended::t2,线程t2结束,主线程可以向下执行t3.join(),等待t3线程结束

    10° 又过了大概20秒,t3线程睡醒,输出Thread ended::t3,线程t3结束,主线程可以向下执行System.out.println(“All threads are dead, exiting main thread”),输出All threads are dead, exiting main thread。此时主线程也执行完毕,程序退出

在C++中,`std::thread::join()` 是用于等待线程完成执行的方法。当调用 `join()` 方法时,调用线程(通常是主线程)会阻塞,直到被调用的线程(例如 `recv_thread`)完成其执行。这种方式常用于确保主线程在子线程完成任务之前不会退出,从而避免资源提前释放或访问无效内存等问题。 具体到 `recv_thread.join()` 的使用场景,假设有一个专门用于接收网络数据的线程(例如通过 `recv` 函数从 socket 接收数据),主线程需要等待该线程完成数据接收任务后才能继续执行后续操作。此时可以使用 `recv_thread.join()` 来实现同步控制。 以下是一个示例代码,展示如何创建一个接收数据的线程并使用 `join()` 等待其完成: ```cpp #include <iostream> #include <thread> #include <sys/socket.h> #include <unistd.h> void receive_data(int sockfd) { char buffer[1024]; ssize_t bytes_received = recv(sockfd, buffer, sizeof(buffer), 0); if (bytes_received > 0) { buffer[bytes_received] = '\0'; std::cout << "Received: " << buffer << std::endl; } else { std::cerr << "Failed to receive data" << std::endl; } close(sockfd); } int main() { int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 假设已经完成 connect 和其他 socket 设置 std::thread recv_thread(receive_data, sockfd); // 主线程等待 recv_thread 完成 recv_thread.join(); // 确保主线程在此处等待,直到 receive_data 执行完毕 std::cout << "Receiver thread finished." << std::endl; return 0; } ``` 上述代码中,`recv_thread.join()` 的作用是阻塞主线程,直到 `recv_thread` 执行完毕。这种做法可以确保主线程不会提前退出,从而避免资源泄漏或程序崩溃的风险[^2]。 ### 相关问题 1. 如何在C++中使用 `std::thread::detach()`,它与 `join()` 有何不同? 2. 在多线程网络编程中,如何确保 `recv` 函数调用的线程安全性? 3. 如果在调用 `join()` 前线程已经结束,会发生什么? 4. 如何在C++中处理线程超时等待,而不是无限期阻塞? 5. 在跨平台开发中,线程的 `join()` 方法在不同操作系统下的行为是否一致?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值