浅谈Java线程Thread.join方法解析

join字面上是加入的意思,我们先看看join方法的解释和实现。

/**
   * Waits for this thread to die.
   * 调用方线程(调用join方法的线程)执行等待操作,直到被调用的线程(join方法所属的线程)结束,再被唤醒
   * <p> An invocation of this method behaves in exactly the same
   * way as the invocation
   *
   *
   * @throws InterruptedException
   *     if any thread has interrupted the current thread. The
   *     <i>interrupted status</i> of the current thread is
   *     cleared when this exception is thrown.
   */
  public final void join() throws InterruptedException {
    join(0);
  }

这里join是调用的

/**
   * Waits at most {@code millis} milliseconds for this thread to
   * die. A timeout of {@code 0} means to wait forever.
   * 等待线程执行结束,或者指定的最大等待时间到了,调用方线程再次被唤醒,如果最大等待时间为0,则只能等线程执行结束,才能被唤醒。
   * <p> This implementation uses a loop of {@code this.wait} calls
   * conditioned on {@code this.isAlive}. As a thread terminates the
   * {@code this.notifyAll} method is invoked. It is recommended that
   * applications not use {@code wait}, {@code notify}, or
   * {@code notifyAll} on {@code Thread} instances.
   *
   *
   */
  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);
      }
    } else {
      while (isAlive()) {
        long delay = millis - now;
        if (delay <= 0) {
          break;
        }
        wait(delay);
        now = System.currentTimeMillis() - base;
      }
    }
  }

可以看到,join方法本身是通过wait方法来实现等待的,这里判断如果线程还在运行中的话,则继续等待,如果指定时间到了,或者线程运行完成了,则代码继续向下执行,调用线程就可以执行后面的逻辑了。

但是在这里没有看到哪里调用notify或者notifyAll方法,如果没有调用的话,那调用方线程会一直等待下去,那是哪里调用了唤醒它的方法呢?通过查证得知,原来在线程结束时,java虚拟机会执行该线程的本地exit方法,

//线程退出函数:
void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
...
//这里会处理join相关的销毁逻辑
ensure_join(this);
...
}
//处理join相关的销毁逻辑
  static void ensure_join(JavaThread* thread) {
   Handle threadObj(thread, thread->threadObj());

   ObjectLocker lock(threadObj, thread);

   thread->clear_pending_exception();

   java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);

   java_lang_Thread::set_thread(threadObj(), NULL);

   //这里就调用notifyAll方法,唤醒等待的线程
   lock.notify_all(thread);

   thread->clear_pending_exception();
  }

这样线程什么时候被唤醒就明白了。下面写个例子看下效果。

public class JoinTest {

  public static void main(String[] args) {

    ThreadBoy boy = new ThreadBoy();
    boy.start();

  }

  static class ThreadBoy extends Thread{
    @Override
    public void run() {

      System.out.println("男孩和女孩准备出去逛街");

      ThreadGirl girl = new ThreadGirl();
      girl.start();

      try {
        girl.join();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }

      System.out.println("男孩和女孩开始去逛街了");
    }
  }

  static class ThreadGirl extends Thread{
    @Override
    public void run() {
      int time = 5000;

      System.out.println("女孩开始化妆,男孩在等待。。。");

      try {
        Thread.sleep(time);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }

      System.out.println("女孩化妆完成!,耗时" + time);

    }
  }

}

执行结果为:

男孩和女孩准备出去逛街
女孩开始化妆,男孩在等待。。。
女孩化妆完成!,耗时5000
男孩和女孩开始去逛街了

就是男孩和女孩准备去逛街,女孩要化妆先,等女孩化妆完成了,再一起去逛街。

那join(time)的用法是怎么样的呢?

public class JoinTest {

  public static void main(String[] args) {

    ThreadBoy boy = new ThreadBoy();
    boy.start();

  }

  static class ThreadBoy extends Thread{
    @Override
    public void run() {

      System.out.println("男孩和女孩准备出去逛街");

      ThreadGirl girl = new ThreadGirl();
      girl.start();

      int time = 2000;
      try {
        girl.join(time);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }

      System.out.println("男孩等了" + time + ", 不想再等了,去逛街了");
    }
  }

  static class ThreadGirl extends Thread{
    @Override
    public void run() {
      int time = 5000;

      System.out.println("女孩开始化妆,男孩在等待。。。");

      try {
        Thread.sleep(time);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }

      System.out.println("女孩化妆完成!,耗时" + time);

    }
  }

}

这里仅仅把join方法换成了join(time)方法,描述改了点,打印的结果是:

男孩和女孩准备出去逛街
女孩开始化妆,男孩在等待。。。
男孩等了2000, 不想再等了,去逛街了
女孩化妆完成!,耗时5000

男孩等了join(time)中的time时间,如果这个time时间到达之后,女孩所在的线程还没执行完,则不等待了,继续执行后面的逻辑,就是不等女孩了,自己去逛街。

由此看出,join方法是为了比较方便的实现两个线程的同步执行,线程1执行,碰到线程2后,等待线程2执行后,再继续执行线程1的执行,加入的意思现在就比较形象化了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 以银行取钱为例模拟Java多线程同步问题完整代码

    简单了解下在操作系统中进程和线程的区别: 进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程.(进程是资源分配的最小单位) 线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小.(线程是cpu调度的最小单位) 线程和进程一样分为五个阶段:创建.就绪.运行.阻塞.终止. 多进程是指操作系统能同时运行多个任务(程序). 多线程是指在同一程序中有多个顺序流在执行.首先存钱取钱的这个操作,应该是线程操作的

  • Java Swing 多线程加载图片(保证顺序一致)

    大二的时候做的课程设计,图片管理器,当时遇到图片很多的文件夹,加载顺序非常慢.虽然尝试用多个Thread加载图片,却无法保证图片按顺序加载.直到今天学会了使用Callable接口和Future接口,于是心血来潮实现了这个功能. 废话不多说,看代码. 多线程加载图片(核心): package com.lin.imagemgr; import java.awt.Dimension; import java.awt.image.BufferedImage; import java.io.File; i

  • Java语言多线程终止中的守护线程实例

    Java中线程分为两种类型:用户线程和守护(服务)线程.通过Thread.setDaemon(false)设置为用户线程;通过Thread.setDaemon(true)设置为守护线程;不设置则默认为用户线程. 结束单线程用 Thread.interrupt() 方法,多线程结束则需要设置守护线程.当不存在用户线程时,守护线程就会全部终结(可以理解为:守护线程是服务线程,用户线程是被服务线程,用户线程(被服务线程)全都没有了,服务线程便没有存在意义而自动终结) 例子: class StopThr

  • Java终止线程实例和stop()方法源码阅读

    了解线程 概念 线程 是程序中的执行线程.Java 虚拟机允许应用程序并发地运行多个执行线程. 线程特点 拥有状态,表示线程的状态,同一时刻中,JVM中的某个线程只有一种状态; ·NEW 尚未启动的线程(程序运行开始至今一次未启动的线程) ·RUNNABLE 可运行的线程,正在JVM中运行,但它可能在等待其他资源,如CPU. ·BLOCKED 阻塞的线程,等待某个锁允许它继续运行 ·WAITING 无限等待(再次运行依赖于让它进入该状态的线程执行某个特定操作) ·TIMED_WAITING 定时

  • Java编程实现多线程TCP服务器完整实例

    相关Java类 Socket public class Socket extends Object ·功能:TCP客户端套接字 ·构造方法: Socket(InetAddress address, int port) 创建一个流套接字并将其连接到指定 IP 地址的指定端口号 ·常用方法: 1.getInetAddress 获得InetAddress的相关信息 2.getInputStream 获得此TCP连接的输入流 3.getOutPutStream 获得此TCP连接的输出流 ServerSo

  • java编程多线程并发处理实例解析

    本文主要是通过一个银行用户取钱的实例,演示java编程多线程并发处理场景,具体如下. 从一个例子入手:实现一个银行账户取钱场景的实例代码. 第一个类:Account.java 账户类: package cn.edu.byr.test; public class Account { private String accountNo; private double balance; public Account(){ } public Account(String accountNo,double

  • Java中后台线程实例解析

    本文研究的主要是Java中后台线程的相关问题,具体介绍如下. 以前从来没有听说过,java中有后台线程这种东西.一般来说,JVM(JAVA虚拟机)中一般会包括俩种线程,分别是用户线程和后台线程.所谓后台线程(daemon)线程指的是:在程序运行的时候在后台提供的一种通用的服务的线程,并且这种线程并不属于程序中不可或缺的部分.因此,当所有的非后台线程结束的时候,也就是用户线程都结束的时候,程序也就终止了.同时,会杀死进程中的所有的后台线程.反过来说,只要有任何非后台线程还在运行,程序就不会结束.不

  • Java线程Dump分析工具jstack解析及使用场景

    jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息,如果是在64位机器上,需要指定选项"-J-d64",Windows的jstack使用方式只支持以下的这种方式: jstack [-l][F] pid 如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题.另外,jstack工具还可以附属到正在运行的j

  • 浅谈Java线程Thread.join方法解析

    join字面上是加入的意思,我们先看看join方法的解释和实现. /** * Waits for this thread to die. * 调用方线程(调用join方法的线程)执行等待操作,直到被调用的线程(join方法所属的线程)结束,再被唤醒 * <p> An invocation of this method behaves in exactly the same * way as the invocation * * * @throws InterruptedException *

  • 浅谈Java线程Thread之interrupt中断解析

    这一篇我们说说Java线程Thread的interrupt中断机制. 中断线程 线程的thread.interrupt()方法是中断线程,将会设置该线程的中断状态位,即设置为true,中断的结果线程是死亡.还是等待新的任务或是继续运行至下一步,就取决于这个程序本身.线程会不时地检测这个中断标示位,以判断线程是否应该被中断(中断标示值是否为true).它并不像stop方法那样会中断一个正在运行的线程. 判断线程是否被中断 判断某个线程是否已被发送过中断请求,请使用Thread.currentThr

  • 浅谈java线程join方法使用方法

    本博客简介介绍一下java线程的join方法,join方法是实现线程同步,可以将原本并行执行的多线程方法变成串行执行的 如图所示代码,是并行执行的 public class ThreadTest { //private static final Long count = 10000L; public static void main(String[] args){ long base = System.currentTimeMillis(); try { ThreadJoin t1 = new

  • 浅谈java线程状态与线程安全解析

    目录 1.线程的几种状态 1.1 线程的状态 1.2 线程状态的转移 2.有关线程安全问题 2.1 一个简单的例子 2.2 造成线程不安全的原因 1.线程的几种状态 1.1 线程的状态 以下就是我们线程所有的状态和意义: NEW 已经创建Thread但未创建线程 RUNNABLE 可工作的. 又可以分成正在工作中和即将开始工作 BLOCKED 等待锁(阻塞状态) WAITING 调用wati方法(阻塞状态) TIMED_WAITING 调用sleep方法(阻塞状态) TERMINATED 系统线

  • 浅谈JAVA 线程状态中可能存在的一些误区

    BLOCKED 和 WAITING 的区别 BLOCKED 和 WAITING 两种状态从结果上来看,都是线程暂停,不会占用 CPU 资源,不过还是有一些区别的 BLOCKED 等待 Monitor 锁的阻塞线程的线程状态,处于阻塞状态的线程正在等待 Monitor 锁进入 synchronized   Block 或者 Method ,或者在调用 Object.wait 后重新进入同步块/方法.简单的说,就是线程等待 synchronized 形式的锁时的状态 下面这段代码中, t1 在等待

  • 浅谈Java线程间通信之wait/notify

    Java中的wait/notify/notifyAll可用来实现线程间通信,是Object类的方法,这三个方法都是native方法,是平台相关的,常用来实现生产者/消费者模式.先来我们来看下相关定义: wait() :调用该方法的线程进入WATTING状态,只有等待另外线程的通知或中断才会返回,调用wait()方法后,会释放对象的锁. wait(long):超时等待最多long毫秒,如果没有通知就超时返回. notify() :通知一个在对象上等待的线程,使其从wait()方法返回,而返回的前提

  • 浅谈java线程中生产者与消费者的问题

    一.概念 生产者与消费者问题是一个金典的多线程协作的问题.生产者负责生产产品,并将产品存放到仓库:消费者从仓库中获取产品并消费.当仓库满时,生产者必须停止生产,直到仓库有位置存放产品:当仓库空时,消费者必须停止消费,直到仓库中有产品. 解决生产者/消费者问题主要用到如下几个技术:1.用线程模拟生产者,在run方法中不断地往仓库中存放产品.2.用线程模拟消费者,在run方法中不断地从仓库中获取产品.3  . 仓库类保存产品,当产品数量为0时,调用wait方法,使得当前消费者线程进入等待状态,当有新

  • 浅谈Java线程池是如何运行的

    异步编程工具在Android开发中目前最被推荐的就是Kotlin协程,在引入Kotlin协程机制前,除了响应式扩展(RxJava)兼任异步编程工具外,Java API中线程与线程池就是最重要异步编程手段.而对于Android平台的Kotlin协程实现来说,依然使用的是线程池来作为任务执行的载体,所以可以将Android平台的Kotlin协程简单的理解是对线程池的一种高度封装. Executors.newFixedThreadPool(10).asCoroutineDispatcher() Dis

  • 浅谈Java线程池的7大核心参数

    前言 java中经常需要用到多线程来处理一些业务,我不建议单纯使用继承Thread或者实现Runnable接口的方式来创建线程,那样势必有创建及销毁线程耗费资源.线程上下文切换问题. 同时创建过多的线程也可能引发资源耗尽的风险,这个时候引入线程池比较合理,方便线程任务的管理. java中涉及到线程池的相关类均在jdk1.5开始的java.util.concurrent包中,涉及到的几个核心类及接口包括: Executor.Executors.ExecutorService.ThreadPoolE

  • 浅谈java中==以及equals方法的用法

    equals 方法是 java.lang.Object 类的方法. 有两种用法说明: (1)对于字符串变量来说,使用"=="和"equals()"方法比较字符串时,其比较方法不同. "=="比较两个变量本身的值,即两个对象在内存中的首地址. "equals()"比较字符串中所包含的内容是否相同. 比如: String s1,s2,s3 = "abc", s4 ="abc" ; s1 =

随机推荐