什么时候要用join()方法?

1,join方法是Thread类中的方法,主线程执行完start()方法,线程就进入就绪状态,虚拟机最终会执行run方法进入运行状态.此时.主线程跳出start方法往下执行

2,两个线程以上,当一个线程需要另一个线程执行的结果时,可以在该线程之前调用另一个线程对象的join方法,如下:

public class TestThread {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new ThreadA("ThreadA");
Thread t2 = new Thread(new ThreadB(),"ThreadB");
t1.start();//主线程调用start方法,开启t1线程,
t1.join();//主线程调用join方法,获取对象t1对象锁,挂起主线程.直到t1线程结束
t2.start();//主线程调用start方法,开启t2线程
t2.join();//主线程调用join,获取t2对象锁,挂起主线程,直到t2线程结束
System.out.println(Thread.currentThread().getName()+"主线程结束");
}
}
如上代码,主线程一路下来调用其他线程的对象的join方法,就会被挂起直到该线程对象所在的线程结束.从而实现线程的顺序执行.

查看join的源码,该方法是同步方法,锁是t1线程所在对象的对象锁,我们可以看到,wait总是在synchronized代码块里面使用,并且在while循环中,有wait()就必然有notify()/notifyAll(),所以同样的一般情况下notify()/notifyAll()也是在synchronized代码块中的while使用的,synchronized,while,wait,notify结合可以实现经典的生产者消费者模式.

public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
....
//主线程调用join方法就拥有了t1对象的锁,wait(0)一直处于等待状态,
//直到某个线程调用了该对象的notify或者notifyAll()
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
....
}
}
        
在join方法中我们看到了wait(),那么一定在某个地方有notify方法,该方法则在jvm源码中,这个段源码的作用是结束线程,在线程退出前需要做一些动作,其中就有调用notify_all的动作,看注释,还将isAlive变为false,
// 位于/hotspot/src/share/vm/runtime/thread.cpp中
void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
// ...
// Notify waiters on thread object. This has to be done after exit() is called
// on the thread (if the thread is the last thread in a daemon ThreadGroup the
// group should have the destroyed bit set before waiters are notified).
// 有一个贼不起眼的一行代码,就是这行
ensure_join(this);
// ...
}
static void ensure_join(JavaThread* thread) {
// We do not need to grap the Threads_lock, since we are operating on ourself.
Handle threadObj(thread, thread->threadObj());
assert(threadObj.not_null(), "java thread object must exist");
ObjectLocker lock(threadObj, thread);
// Ignore pending exception (ThreadDeath), since we are exiting anyway
thread->clear_pending_exception();
// Thread is exiting. So set thread_status field in java.lang.Thread class to TERMINATED.
java_lang_Thread::set_thread_status(threadObj(), java_lang_Thread::TERMINATED);
// Clear the native thread instance - this makes isAlive return false and allows the join()
// to complete once we've done the notify_all below
java_lang_Thread::set_thread(threadObj(), NULL);
// 唤醒其他线程
lock.notify_all(thread);
// Ignore pending exception (ThreadDeath), since we are exiting anyway
thread->clear_pending_exception();
}
 

  由此我们即看到了notify,也看到了isAlive变为false,所以主线程再挂起的时候,会等待该对象线程t1结束并调用notify,唤醒之后则跳出循环检测isAlive为false则跳

出循环结束join方法,这就实现了线程的顺序执行,

码解读完毕

从代码中的while(isAlive){wait()}说起,

如果主线程在等待状态,被其他线程唤醒呢,所以前文我说,notify也一般配合synchronized,while使用,这个一般情况下是消费者生产者模式,但是在jvm源码中并没

有这么用,这是要等待t1线程结束前调用,所以这个isAlive就是保证一定被t1线程结束唤醒,而其他线程唤醒while判断之后再次进入挂起状态.

几个问题的辨析, 

        1,线程之间的竞争是同一个对象锁的竞争或者同一个类锁的竞争,与其他对象的锁无关.其他线程抢别的对象的锁,执行别对象的wait/notify方法都与竞争本对象

锁的线程无关.

2, 锁分为对象锁,类锁,锁的使用者叫线程,锁拦截的对象也是线程,不是什么方法,或者其他,.

对象锁,表示对象里面所有被synchronized(this)修饰的代码块和synchronized 修饰的普通方法块,不能同时被两个线程执行.详情见:synchronized的使用方法和作用域:

https://mp.weixin.qq.com/s?__biz=MzI4NTEzMjc5Mw==&mid=2650554746&idx=1&sn=8e45e741ca9f058dba1f3ebbea0e9f07&chksm=f3f833ecc48fbafa295e355c1cdd52dc4259f161dafdc1703d181a5e9f4f76563c98493bd221&token=2005887224&lang=zh_CN#rd

类锁是被synchronized(MyClass.class)和static synchronized 修饰的静态方法,不能同时被两个线程执行,详情见:synchronized的使用方法和作用域

https://mp.weixin.qq.com/s?__biz=MzI4NTEzMjc5Mw==&mid=2650554746&idx=1&sn=8e45e741ca9f058dba1f3ebbea0e9f07&chksm=f3f833ecc48fbafa295e355c1cdd52dc4259f161dafdc1703d181a5e9f4f76563c98493bd221&token=2005887224&lang=zh_CN#rd

3,线程释放锁的场景,

a,执行完同步代码块,

b,在执行同步代码块的过程中,遇到异常而导致线程终止,锁也会被释放

c在执行同步代码块的过程中,执行了锁所属对象的wait方法,这个线程会释放对象锁,而此线程对象会进入线程等待池中,等待被唤醒

notify和notifyAll的区别

我们首先得明确,一个对象只有一个锁,参与该对象锁竞争的线程,与参与其他对象锁竞争的线程无关.线程竞争,对应一个对象的一个锁.

有n个线程参与竞争同一对象的锁,t1率先抢到了锁,锁池中有n-1个线程在等待竞争锁,t1调用了wait()方法t1线程挂起,等待池中有一线程等待被唤醒.t2线程,获得了锁,锁池中有n-2个线程等待竞争锁,t2线程调用了wait方法挂起,等待池中有两个线程等待被唤醒,

此时t3线程获得了锁,

如果t3线程在同步代码块中调用的是notify则只会随机唤醒等待池中的一个线程移入锁池中参与锁竞争,有可能是t1,也有可能是t2,等待池中的线程为1

如果t3线程在同步代码块中调用的notifyAll,则会同时唤醒t1,t2移入锁池中参与竞争,等待池中的线程为零,

公众号:

线程中的join方法,与synchronized和wait()和notify()的关系的更多相关文章

  1. 模拟做饭系统(java+线程中的join方法)

    (一)项目框架分析 妈妈要去做饭,发现没有酱油,让儿子去买酱油,然后回来做饭. 根据面向对象的思想,有两个对象,妈妈和儿子 主要有两个方法: (一)没有线程控制(即儿子没有买酱油回来妈妈就做好饭了)+ ...

  2. 线程中的join方法

    join方法的作用是同步线程. 1.不使用join方法:当设置多个线程时,在一般情况下(无守护线程,setDeamon=False),多个线程同时启动,主线程执行完,会等待其他子线程执行完,程序才会退 ...

  3. 线程中的join()

    http://blog.itpub.net/31555134/viewspace-2221319/ 一直对join()方法不是很理解,在A线程中, B线程调用了join()方法,然后在内部实际是wai ...

  4. Java多线程中的join()方法

    一.join()方法介绍 join() 定义在Thread.java中.join()方法把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程.比如在线程B中调用了线程A的join( ...

  5. Java并发编程--多线程中的join方法详解

    Java Thread中, join()方法主要是让调用该方法的thread在完成run方法里面的部分后, 再执行join()方法后面的代码 例如:定义一个People类,run方法是输出姓名年龄. ...

  6. java线程学习之join方法

    join()方法表示一个线程要加入另一个线程,直到被加入的线程执行完毕. 这个概念不好理解的话看面这个例子 public class TestJoin { public static void mai ...

  7. Thread类中的join方法

    package charpter06; //类实现接口public class Processor implements Runnable { // 重写接口方法 @Override public v ...

  8. 3-等待线程终止的join方法

    等待线程终止的join方法 在项目实践中经常会遇到一个场景,就是需要等待某几件事完成之后才能继续往下执行,比如线程加载资源等等. package com.heiye.learn1; public cl ...

  9. jQuery中的join方法

    和JS 中的JOIN 方法一样,将一数组按照JOIN的参数连接起来.比如: var arr = [ "a", "b", "c", " ...

随机推荐

  1. Linux(CentOS)启动时自动执行脚本(rc.local)

    一.Linux开机启动有多种方法,比如我设置mysql开机启动为:chkconfig --level 35 mysqld on 二.下面说说通过rc.local文件进行开机启动: 1.首先创建一个启动 ...

  2. laravel5.8ajax请求auth认证返回302的解决方法。

    注册 /app/Http/Controller/Auth/RegisterController.php <?php namespace App\Http\Controllers\Auth; us ...

  3. springboot踩坑记

    1. @ConditionalOnProperty 根据配置加载不同的 bean 场景:对 redis 配置进行封装,实现自动化配置,能兼容哨兵模式和集群模式.想到在 redis 配置中加一个 red ...

  4. idea 模糊搜索 ctrl + f(单词不完整搜索不到的解决办法)

    1,现象描述,笔者在用 idea 的 ctrl + f 搜索文件的内容时,发现了很神奇的问题,就是字符串必须输入完整才能搜索到,输入一半,哪怕是个字母输入了9个也搜不到 2,可以发现,就差一个字母 h ...

  5. nginx入门系列之安装与卸载

    目录 通过包管理器安装 安装nginx 卸载nginx 从源码编译安装 准备安装环境 执行编译安装 制作nginx免安装包 官方安装手册:https://nginx.org/en/docs/insta ...

  6. vue做页面按钮权限--分析

    import * as types from '../mutation-types' const state = { btnCode: getBtnCode(), } const mutations ...

  7. Bitmap通过getWidth和getHeight获取尺寸不符 -- 误以为是Matrix失效(哈)

    参考:Bitmap通过getWidth和getHeight获取尺寸不符 因为项目需要我想做个这样的地图出来 这个图的的分辨率是1190 * 666的 在地图上定位,我底图是固定分辨率的,那么算个坐标就 ...

  8. 选择类排序 (简单选择排序,堆排序)— c语言实现

    选择类排序包括: (1)  简单选择排序 (2)树形选择排序 (3)堆排序 简单选择排序: [算法思想]:在第 i 趟简单选择排序中,从第 i 个记录开始,通过 n - i 次关键字比较,从 n - ...

  9. IDEA debug断点调试技巧

    Debug用来追踪代码的运行流程,通常在程序运行过程中出现异常,启用Debug模式可以分析定位异常发生的位置,以及在运行过程中参数的变化.通常我们也可以启用Debug模式来跟踪代码的运行流程去学习三方 ...

  10. 关于使用K8S的技术流程

    部署Gogs版本管理系统 地址:https://gogs.io/docs 部署Harbor私有仓库 地址:https://github.com/goharbor/harbor/blob/master/ ...