JAVA并发-join
概念
join方法,一种特殊的wait,当前运行线程调用另一个线程的join方法,当前线程进入阻塞状态直到调用join方法的线程结束,再继续执行。
一般情况下,都是主线程创建一个子线程,子线程调用join方法,主线程会进入阻塞状态,直到子线程运行结束。
简单案例
public class JoinThreadDemo {
public static void main(String[] args) {
JoinRunnable runnable1 = new JoinRunnable();
Thread thread1 = new Thread(runnable1, "线程1");
System.out.println("主线程开始执行!");
thread1.start();
// try {
// thread1.join();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println("主线程执行结束!");
}
static final class JoinRunnable implements Runnable {
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name + "开始执行!");
for (int i = 1; i <= 5; i++) {
System.out.println(name + "执行了[" + i + "]次");
}
}
}
}
Output:
主线程开始执行!
主线程执行结束!
线程1开始执行!
线程1执行了[1]次
线程1执行了[2]次
线程1执行了[3]次
线程1执行了[4]次
线程1执行了[5]次
取消注释,调用join方法:
主线程开始执行!
线程1开始执行!
线程1执行了[1]次
线程1执行了[2]次
线程1执行了[3]次
线程1执行了[4]次
线程1执行了[5]次
主线程执行结束!
替代join案例
public class JoinThreadDemo {
public static void main(String[] args) {
JoinRunnable runnable1 = new JoinRunnable();
Thread thread1 = new Thread(runnable1, "线程1");
System.out.println("主线程开始执行!");
thread1.start();
try {
synchronized (thread1) {
while (thread1.isAlive()) {
System.out.println("begin wait");
//主线程持有thread1对象锁,阻塞,一直到thread1运行结束,jvm唤醒
thread1.wait(0);
System.out.println("thread wait");
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程执行结束!");
}
static final class JoinRunnable implements Runnable {
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name + "开始执行!");
for (int i = 1; i <= 5; i++) {
System.out.println(name + "执行了[" + i + "]次");
}
}
}
}
Output:
主线程开始执行!
begin wait
线程1开始执行!
线程1执行了[1]次
线程1执行了[2]次
线程1执行了[3]次
线程1执行了[4]次
线程1执行了[5]次
thread wait
主线程执行结束!
在thread1调用wait后,主线程阻塞,一直到子线程thread1运行结束退出以后,jvm会自动唤醒阻塞在thread1对象上的线程
那么有没有可能不使用thread1对象阻塞呢?
下面就是不使用thread1对象阻塞主线程的案例
替代join案例2
public class JoinThreadDemo {
public static void main(String[] args) {
Object lock = new Object();
Thread thread1 = new Thread(() -> {
String name = Thread.currentThread().getName();
System.out.println(name + "开始执行!");
for (int i = 1; i <= 5; i++) {
System.out.println(name + "执行了[" + i + "]次");
}
}, "线程1");
System.out.println("主线程开始执行!");
thread1.start();
//thread2自旋唤醒阻塞在lock对象上的主线程
Thread thread2 = new Thread(new Thread() {
@Override
public void run() {
while (!thread1.isAlive() && !Thread.currentThread().isInterrupted()) {
synchronized (lock) {
System.out.println("enter");
lock.notifyAll();
System.out.println("exit");
}
}
}
}, "线程2");
thread2.start();
try {
synchronized (lock) {
while (thread1.isAlive()) {
System.out.println("bb");
lock.wait();
//停止thread2自旋
thread2.interrupt();
System.out.println("tt");
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程执行结束!");
}
}
Output:
主线程开始执行!
bb
线程1开始执行!
线程1执行了[1]次
线程1执行了[2]次
线程1执行了[3]次
线程1执行了[4]次
线程1执行了[5]次
enter
exit
enter
exit
tt
主线程执行结束!
这里添加了一个线程thread2用于专门自旋唤醒主线程
用于替换案例一中的,thread1线程结束后,jvm唤醒主线程操作
join原理
阻塞主线程
Thread类中,join源码:
//Thread类中
public final void join() throws InterruptedException {
join(0);
}
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) { //这个分支是无限期等待直到b线程结束
while (isAlive()) {
wait(0);
}
} else { //这个分支是等待固定时间,如果b没结束,那么就不等待了。
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
join成员方法中有synchronized,锁定的对象为调用该方法的对象,即子线程thread1,即主线程持有了thread1对象锁
可以自己调试,发现进入到join源码的线程为主线程
唤醒主线程
子线程thread1执行完毕的时候,jvm会自动唤醒阻塞在thread1对象上的线程,在我们的例子中也就是主线程。至此,thread1线程对象被notifyall了,那么主线程也就能继续跑下去了
参考
who and when notify the thread.wait() when thread.join() is called?
JAVA并发-join的更多相关文章
- java并发:join源码分析
join join join是Thread方法,它的作用是A线程中子线程B在运行之后调用了B.join(),A线程会阻塞直至B线程执行结束 join源码(只有继承Thread类才能使用) 基于open ...
- Java 并发编程:线程间的协作(wait/notify/sleep/yield/join)
Java并发编程系列: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) Java 并发编程 ...
- Java并发编程之线程生命周期、守护线程、优先级、关闭和join、sleep、yield、interrupt
Java并发编程中,其中一个难点是对线程生命周期的理解,和多种线程控制方法.线程沟通方法的灵活运用.这些方法和概念之间彼此联系紧密,共同构成了Java并发编程基石之一. Java线程的生命周期 Jav ...
- Java并发编程原理与实战二十一:线程通信wait¬ify&join
wait和notify wait和notify可以实现线程之间的通信,当一个线程执行不满足条件时可以调用wait方法将线程置为等待状态,当另一个线程执行到等待线程可以执行的条件时,调用notify可以 ...
- 【转】Java 并发编程:线程间的协作(wait/notify/sleep/yield/join)
一.线程的状态 Java中线程中状态可分为五种:New(新建状态),Runnable(就绪状态),Running(运行状态),Blocked(阻塞状态),Dead(死亡状态). New:新建状态,当线 ...
- Java 并发编程 -- Fork/Join 框架
概述 Fork/Join 框架是 Java7 提供的一个用于并行执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架.下图是网上流传的 Fork Join 的 ...
- Java并发编程(07):Fork/Join框架机制详解
本文源码:GitHub·点这里 || GitEE·点这里 一.Fork/Join框架 Java提供Fork/Join框架用于并行执行任务,核心的思想就是将一个大任务切分成多个小任务,然后汇总每个小任务 ...
- Java并发——Fork/Join框架
为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/ShiJiaqi. http://www.cnblogs.com/shijiaqi1066/p/4631466. ...
- Java并发——Fork/Join框架与ForkJoinPool
为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/ShiJiaqi. http://www.cnblogs.com/shijiaqi1066/p/4631466. ...
随机推荐
- spring cloud gateway 全局过滤器
全局过滤器作用于所有的路由,不需要单独配置,我们可以用它来实现很多统一化处理的业务需求,比如权限认证,IP访问限制等等. 接口定义类:org.springframework.cloud.gateway ...
- inputType导致TextView不能多行显示
今天遇到一个问题很纳闷,那就是TextView不能自动换行多行显示,因为我的印象是TextView默认是可以自动换行多行显示的,今儿个怎么就不行呢. 最终找到原因,是因为设置了inputType属性导 ...
- springcloud2.x之management.security.enabled=false报错处理
1. springcloud1.5.x的消息总线配置是 # RabbitMq的地址.端口,用户名.密码 spring.rabbitmq.host=localhost spring.rabbitmq.p ...
- 分布式事物解决方案-TCC
分布式框架下,如何保证事物一致性一直是一个热门话题.当然事物一致性解决方案有很多种(请参考:分布式事物一致性设计思路),我们今天主要介绍TCC方案解决的思路.以下是参与设计讨论的一种解决思路,大家有问 ...
- IntelliJ IDEA 创建java application
1.打开IntelliJ IDEA,选择File——New——Project... 2.选择左侧的java,然后在右侧的Project SDK 中添加 java 环境目录,点击Next 3. 在下面的 ...
- IDEA 2019注册码(2020年4月过期)
IDEA 2019注册码(2020年4月过期) 812LFWMRSH-eyJsaWNlbnNlSWQiOiI4MTJMRldNUlNIIiwibGljZW5zZWVOYW1lIjoi5q2j54mII ...
- 目标检测算法之Fast R-CNN和Faster R-CNN原理
一.Fast R-CNN原理 在SPPNet中,实际上特征提取和区域分类两个步骤还是分离的.只是使用ROI池化层提取了每个区域的特征,在对这些区域分类时,还是使用传统的SVM作为分类器.Fast R- ...
- Go语言-1-标识符与变量
目录 1. Go标识符 1.1 Go关键字 1.2 常量标识符(4个) 1.3 空白标识符(1个) 1.4 内置数据类型标识符 1.5 内置函数(15个) 2. Go语言操作符 3. Go语言变量 3 ...
- Windows git和cmd代理设置
linux的比较简单,直接修改配置文件即可,这里就不再赘述 设置Git代理 http代理: 临时代理: export http_proxy=http://127.0.0.1:7777 export h ...
- C# 通过方法的字符串名动态调用方法 反射实现
通过字符串执行方法,在可能会调用不同方法时对不同方法进行调用 以下为实例: public class ABC { string str = "通过反射执行的方法"; //Class ...