技术自查番外篇五:join()方法原理

5 篇文章 0 订阅
5 篇文章 0 订阅

Join()方法

作用

作用是:线程并行改为串行,并等待另一个线程执行结束

这句话看似简单,但实际上也容易 把人误导,认为只变得一个线程在执行所有方式,但实际上不然,本质上还是多个线程同时运行

原因/原理

Join()方法底层调用的是wait()方法,把当前主线程状态更改为等待状态,且等待子线程运行完毕唤醒

源码

public final synchronized void join(long var1) throws InterruptedException {
    long var3 = System.currentTimeMillis();
    long var5 = 0L;
    if (var1 < 0L) {
        throw new IllegalArgumentException("timeout value is negative");
    } else {
        if (var1 == 0L) { //由于上一步传入参数为0,因此调用当前判断
            while(this.isAlive()) { //判断子线程是否存活
                this.wait(0L); //调用wait(0)方法
            }
        } else {
            while(this.isAlive()) {
                long var7 = var1 - var5;
                if (var7 <= 0L) {
                    break;
                }

                this.wait(var7);
                var5 = System.currentTimeMillis() - var3;
            }
        }

    }
}

public final synchronized void join(long var1, int var3) throws InterruptedException {
    if (var1 < 0L) {
        throw new IllegalArgumentException("timeout value is negative");
    } else if (var3 >= 0 && var3 <= 999999) {
        if (var3 >= 500000 || var3 != 0 && var1 == 0L) {
            ++var1;
        }

        this.join(var1);
    } else {
        throw new IllegalArgumentException("nanosecond timeout value out of range");
    }
}

public final void join() throws InterruptedException {
    this.join(0L);
}

可以从源码看出,最后还是调用了this.wait(0)L方法,让主线程进入等待状态。

再看下isAlive()方法,最后发现该方法是本地方法(调用最底层C++的函数方法), 判断子线程是否还存活

示例代码

/**
 * @author Leo
 * @description join案例,底层实际调用wait
 * @createDate 2021/9/1 16:44
 **/
public class JoinThread implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "..." + i);
        }
    }

    /**
     * 线程的join用法,运行结果是,完全输出线程1结果后,再输出main结果
     * 让权,底层调用的wait方法
     * 不涉及锁
     *
     * @param args
     */
    public static void main(String[] args) throws Exception {
        JoinThread thread = new JoinThread();
        Thread t1 = new Thread(thread, "join线程");
        for (int j = 0; j < 10; j++) {
            System.out.println(Thread.currentThread().getName() + "..." + j);
            if (j == 1) {
                t1.start();
                t1.join();
            }
        }
    }
}
日志打印
main...0
main...1
join线程...0
join线程...1
join线程...2
join线程...3
join线程...4
join线程...5
join线程...6
join线程...7
join线程...8
join线程...9
main...2
main...3
main...4
main...5
main...6
main...7
main...8
main...9

步骤

1. 主线程执行到j==1时,开启子线程且运行,同时主线程调用Join()方法

2. 主线程运行join()方法,先是调用isAlive()方法,判断子线程是否存活,如果存活,则调用wait()方法,使主线程自身进入等待状态

3. 子线程运行完毕,唤醒主线程

这里就存在问题,如何唤醒主线程

其实在《技术自查第四篇:线程基础篇》已经解答过该问题

我们都知道每个对象都存在唯一一个monitor对象,monitor对象是锁的关键,这里就不具体讲述Monitor对象了。

查看ObjectMonitor对象的C++源码

ObjectMonitor() {
    _count        = 0; //记录数
    _recursions   = 0; //锁的重入次数
    _owner        = NULL; //指向持有ObjectMonitor对象的线程 
    _WaitSet      = NULL; //调用wait后,线程会被加入到_WaitSet
    _EntryList    = NULL ; //等待获取锁的线程,会被加入到该列表
}

对应的java类

由于主线程调用了wait()方法,所以被monitor监控器放到了_waitSet数组中,进入等待状态,一旦其他线程调用notify()或者notifyAll()方法即可唤醒主线程。

我们再看下Thread类的exit()

/**
* This method is called by the system to give a Thread
* a chance to clean up before it actually exits.
* 这个方法由系统调用,当该线程完全退出前给它一个机会去释放空间。
*/
private void exit() {
        if (group != null) {                //线程组在Thread初始化时创建,存有创建的子线程
            group.threadTerminated(this);   //调用threadTerminated()方法
            group = null;
        }
        /* Aggressively null out all reference fields: see bug 4006245 */
        target = null;
        /* Speed the release of some of these resources */
        threadLocals = null;
        inheritableThreadLocals = null;
        inheritedAccessControlContext = null;
        blocker = null;
        uncaughtExceptionHandler = null;
    }

点击进入group.threadTerminated(this)方法,

/** Notifies the group that the thread {@code t} has terminated.
  * 通知线程组,t线程已经终止。
  *
void threadTerminated(Thread t) {
        synchronized (this) {
            remove(t);                          //从线程组中删除此线程

            if (nthreads == 0) {                //当线程组中线程数为0时
                notifyAll();                    //唤醒所有待定中的线程
            }
            if (daemon && (nthreads == 0) &&
                (nUnstartedThreads == 0) && (ngroups == 0))
            {
                destroy();
            }
        }
    }

最后发现底层调用了NotifyAll()方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值