栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

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++的函数方法), 判断子线程是否还存活

示例代码
public class JoinThread implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "..." + i);
        }
    }

    
    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()

private void exit() {
        if (group != null) {                //线程组在Thread初始化时创建,存有创建的子线程
            group.threadTerminated(this);   //调用threadTerminated()方法
            group = null;
        }
        
        target = null;
        
        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()方法

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/281778.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号