join方法的实现原理「建议收藏」

join方法的实现原理「建议收藏」于Java开发人员,多线程应该是必须熟练应用的知识点,特别是开发基于Java语言的产品。本文将深入浅出的表述Java多线程的知识点,在后续的系列里将侧重于Java5由DougLea教授提供的Concurrent并行包的设计思想以及具体实现与应用。如何才能深入浅出呢,我的理解是带着问题,而不是泛泛的看。所以该系列基本以解决问题为主,当然我也非常希望读者能够提出更好的解决问题的方案以及提

大家好,又见面了,我是你们的朋友全栈君。

于Java开发人员,多线程应该是必须熟练应用的知识点,特别是开发基于Java语言的产品。本文将深入浅出的表述Java多线程的知识点,在后续的系列里将侧重于Java5由Doug Lea教授提供的Concurrent并行包的设计思想以及具体实现与应用。
如何才能深入浅出呢,我的理解是带着问题,而不是泛泛的看。所以该系列基本以解决问题为主,当然我也非常希望读者能够提出更好的解决问题的方案以及提出更多的问题。由于水平有限,如果有什么错误之处,请大家提出,共同讨论,总之,我希望通过该系列我们能够深入理解Java多线程来解决我们实际开发的问题。
作为开发人员,我想没有必要讨论多线程的基础知识,比如什么是线程? 如何创建等 ,这些知识点是可以通过书本和Google获得的。本系列主要是如何理深入解多线程来帮助我们平时的开发,比如线程池如何实现? 如何应用锁等。

(1)方法Join是干啥用的? 简单回答,同步,如何同步? 怎么实现的? 下面将逐个回答。
自从接触Java多线程,一直对Join理解不了。JDK是这样说的:
join
public final void join(long millis)throws InterruptedException
Waits at most millis milliseconds for this thread to die. A timeout of 0 means to wait forever.
大家能理解吗? 字面意思是等待一段时间直到这个线程死亡,我的疑问是那个线程,是它本身的线程还是调用它的线程的,上代码:

package concurrentstudy;
/** * * @author vma */
public class JoinTest { 
   
    public static void main(String[] args) {
        Thread t = new Thread(new RunnableImpl());
        t.start();
        try {
            t.join(1000);
            System.out.println("joinFinish");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();

        }
    }
}
class RunnableImpl implements Runnable {

    @Override
    public void run() {
        try {
            System.out.println("Begin sleep");
            Thread.sleep(1000);
           System.out.println("End sleep");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

结果是:
Begin sleep
End sleep
joinFinish
明白了吧,当main线程调用t.join时,main线程等待t线程,等待时间是1000,如果t线程Sleep 2000呢

 public void run() {
        try {
            System.out.println("Begin sleep");
            // Thread.sleep(1000);
            Thread.sleep(2000);
           System.out.println("End sleep");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

结果是:
Begin sleep
joinFinish
End sleep
也就是说main线程只等1000毫秒,不管T什么时候结束,如果是t.join()呢, 看代码:
public final void join() throws InterruptedException {
join(0);
}
就是说如果是t.join() = t.join(0) 0 JDK这样说的 A timeout of 0 means to wait forever 字面意思是永远等待,是这样吗?
其实是等到t结束后。
这个是怎么实现的吗? 看JDK代码:

    /** * Waits at most <code>millis</code> milliseconds for this thread to * die. A timeout of <code>0</code> means to wait forever. * * @param millis the time to wait in milliseconds. * @exception 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 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(小提示:Object 提供的方法)。 当main线程调用t.join时候,main线程会获得线程对象t的锁(wait 意味着拿到该对象的锁),调用该对象的wait(等待时间),直到该对象唤醒main线程,比如退出后。

这就意味着main 线程调用t.join时,必须能够拿到线程t对象的锁,如果拿不到它是无法wait的,刚开的例子t.join(1000)不是说明了main线程等待1秒,如果在它等待之前,其他线程获取了t对象的锁,它等待时间可不就是1毫秒了。上代码介绍:

/* * To change this template, choose Tools | Templates * and open the template in the editor. */
package concurrentstudy;
/** * * @author vma */
public class JoinTest { 
   
    public static void main(String[] args) {
        Thread t = new Thread(new RunnableImpl());
       new ThreadTest(t).start();
        t.start();
        try {
            t.join();
            System.out.println("joinFinish");
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();

        }
    }
}
class ThreadTest extends Thread { 
   

    Thread thread;

    public ThreadTest(Thread thread) {
        this.thread = thread;
    }

    @Override
    public void run() {
        holdThreadLock();
    }

    public void holdThreadLock() {
        synchronized (thread) {
            System.out.println("getObjectLock");
            try {
                Thread.sleep(9000);

            } catch (InterruptedException ex) {
             ex.printStackTrace();
            }
            System.out.println("ReleaseObjectLock");
        }

    }
}

class RunnableImpl implements Runnable { 
   

    @Override
    public void run() {
        try {
            System.out.println("Begin sleep");
            Thread.sleep(2000);
           System.out.println("End sleep");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }
}

在main方法中 通过new ThreadTest(t).start();实例化ThreadTest 线程对象, 它在holdThreadLock()方法中,通过 synchronized (thread),获取线程对象t的锁,并Sleep(9000)后释放,这就意味着,即使
main方法t.join(1000),等待一秒钟,它必须等待ThreadTest 线程释放t锁后才能进入wait方法中,它实际等待时间是9000+1000 MS
运行结果是:
getObjectLock
Begin sleep
End sleep
ReleaseObjectLock

isalive方法

单纯从代码上看: * 如果线程被生成了,但还未被起动,isAlive()将返回false,调用它的join()方法是没有作用的。将直接继续向下执行。
从源码里看,wait方法中有参数,也就是不用唤醒谁,只是不再执行wait,向下继续执行而已。 * 在join()方法中,对于isAlive()和wait()方法的作用对象是个比较让人困惑的问题:

isAlive()方法的签名是:public final native boolean isAlive(),也就是说isAlive()是判断当前线程的状态,也就是bt的状态。

wait()方法在jdk文档中的解释如下:

Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object. In other words, this method behaves exactly as if it simply performs the call wait(0).

The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.

在这里,当前线程指的是main线程。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/142932.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 装上这 8 个插件,PyCharm才真的是无敌的存在!

    装上这 8 个插件,PyCharm才真的是无敌的存在!给IT入门加星标,提升编程技能ChinesePlugin经常听到很多初学者抱怨说,PyCharm怎么是全英文的?学起来好难啊。在以前,我会跟他们说,学习编程语言,英文是一项非常重要的能…

    2022年6月24日
    29
  • 系统可用性几个9

    系统可用性几个9经常看到各种技术文章或者分布式系统介绍说系统的可用性达到了多少个9,那么所谓”几个9“到底是怎么计算的?又意味着什么?我们简单计算分析下看看。所谓”1个9“是指90%,”2个9“是指99%,”3个9“是指99.9%,依次类推。可用性的反面是故障时间,网站或者分布式系统会因为很多原因导致不可用,比如:程序bug;运维更新错误;环境配置升级变化;机器硬件故障;被恶意攻击;网关不小心踢掉了网线/电源插座…

    2022年7月12日
    32
  • 1521端口已被占用解决方案

    1521端口已被占用解决方案1.先查看端口情况通过netstat-ano,查看1521端口是否被其他程序占用;如果被占用几下进程ID通过tasklist|findstr”进程ID”查找到程序名,KILLIT;2….

    2022年5月18日
    178
  • 大数据采集技术概述「建议收藏」

    大数据采集技术概述「建议收藏」大数据采集是指从传感器和智能设备、企业在线系统、企业离线系统、社交网络和互联网平台等获取数据的过程。数据包括RFID数据、传感器数据、用户行为数据、社交网络交互数据及移动互联网数据等各种类型的结构化、半结构化及非结构化的海量数据。不但数据源的种类多,数据的类型繁杂,数据量大,并且产生的速度快,传统的数据采集方法完全无法胜任。所以,大数据采集技术面临着许多技术挑战,一方面需要保证数据…

    2022年6月24日
    46
  • 迁移学习——Domain Adaptation

    DomainAdaptation在经典的机器学习问题中,我们往往假设训练集和测试集分布一致,在训练集上训练模型,在测试集上测试。然而在实际问题中,测试场景往往非可控,测试集和训练集分布有很大差异,这时候就会出现所谓过拟合问题:模型在测试集上效果不理想。以人脸识别为例,如果用东方人人脸数据训练,用于识别西方人,相比东方人识别性能会明显下降。当训练集和测试集分布不一致的情况下,通过在训练…

    2022年4月16日
    41
  • 达梦数据库备份实操「建议收藏」

    达梦数据库备份实操「建议收藏」达梦数据库备份方式:物理备份,逻辑备份物理备份 冷备:(dmap服务打开的状态下,数据库是关闭的) 热备:(dmap服务一定是打开的,数据库是打开的,数据库要开归档) 逻辑备份 导入导出:dexpdimp 集群:数据守护(dw),DSC(RAC) 达梦支持第三方的备份工具:如第三方备份一体机备份首先要开归档 SQL>alterdatabasemount;#mo…

    2022年5月14日
    38

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号