java多线程基本概述(七)——join()方法
在很多情况下,主线程创建并启动子线程,如果子线程中有大量的耗时运算,主线程将早于子线程结束,如果想让主线程等待子线程结束后再结束,那么我们可以使用join()方法。调用join()方法的意思是当前线程使调用了该方法的线程执行完成然后再执行自己本身。api文档如下:
public final void join(long millis,
int nanos)
throws InterruptedException
Waits at most millis milliseconds plus nanos nanoseconds for this thread to die.
This implementation uses a loop of this.wait calls conditioned on this.isAlive. As a thread terminates the this.notifyAll method is invoked. It is recommended that applications not use wait, notify, or notifyAll on Thread instances. Parameters:
millis - the time to wait in milliseconds
nanos - 0-999999 additional nanoseconds to wait
Throws:
IllegalArgumentException - if the value of millis is negative, or the value of nanos is not in the range 0-999999
InterruptedException - if any thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown
简单翻译如下:调用该方法的线程会等待millils+nanos的时间,直到该线程执行完(调用该方法的Thread.isAlive()方法返回false时)。该方法的实现是使用循环判断该线程isAlive()方法,如果为true,那么就调用wait()方法进行等待。当线程结束时,notifyAll()方法被调用。如果调用join()后再调用notify()/notifyAll()则可能会时join()方法失效。源码如下:
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;
}
}
}
例子:
package soarhu;
class Service{
void readMethod(){
try {
for (int i = 0; i < 5; i++) {
System.out.println(i);
}
}catch (Exception e){
e.printStackTrace();
}
}
}
public class Test {
public static void main(String[] args) throws Exception {
Service o = new Service();
new Thread(){
@Override
public void run() {
o.readMethod();
}
}.start();
System.out.println("main.......");
}
}
输出结果:
main.......
0
1
2
3
4 Process finished with exit code 0
结果分析:可知主线程方法先被执行完毕。如果要使子线程先执行完,然后执行主线程。那么可以调用join()方法。
package soarhu;
class Service{
void readMethod(){
try {
for (int i = 0; i < 25; i++) {
Thread.yield();
System.out.println(i+" "+Thread.currentThread().getName());
}
}catch (Exception e){
e.printStackTrace();
}
}
}
public class Test {
public static void main(String[] args) throws Exception {
Service o = new Service();
Thread t = new Thread(){
@Override
public void run() {
o.readMethod();
}
};
t.start();
t.join();
for (int i = 0; i < 10; i++) {
System.out.println("main......."+i);
}
}
}
输出结果:
0 Thread-0
1 Thread-0
2 Thread-0
3 Thread-0
4 Thread-0
5 Thread-0
6 Thread-0
7 Thread-0
8 Thread-0
9 Thread-0
10 Thread-0
11 Thread-0
12 Thread-0
13 Thread-0
14 Thread-0
15 Thread-0
16 Thread-0
17 Thread-0
18 Thread-0
19 Thread-0
20 Thread-0
21 Thread-0
22 Thread-0
23 Thread-0
24 Thread-0
main.......0
main.......1
main.......2
main.......3
main.......4
main.......5
main.......6
main.......7
main.......8
main.......9 Process finished with exit code 0
可知确实是等待子线程执行完后才执行主线程。我们不用join()也可以参考api源码来实现join().如下:
public class Test {
public static void main(String[] args) throws Exception {
Service o = new Service();
Thread t = new Thread() {
@Override
public void run() {
o.readMethod();
}
};
t.start();
//t.join();
synchronized (t) {
do {
t.wait();
} while (t.isAlive());
}
for (int i = 0; i < 10; i++) {
Thread.yield();
System.out.println("main......." + i);
}
}
}
输出结果:
0 Thread-0
1 Thread-0
2 Thread-0
3 Thread-0
4 Thread-0
5 Thread-0
6 Thread-0
7 Thread-0
8 Thread-0
9 Thread-0
10 Thread-0
11 Thread-0
12 Thread-0
13 Thread-0
14 Thread-0
15 Thread-0
16 Thread-0
17 Thread-0
18 Thread-0
19 Thread-0
20 Thread-0
21 Thread-0
22 Thread-0
23 Thread-0
24 Thread-0
main.......0
main.......1
main.......2
main.......3
main.......4
main.......5
main.......6
main.......7
main.......8
main.......9 Process finished with exit code 0
可见join()方法的实现方式很简单,就是加一个同步方法,再内部循环判断该线程是否结束,如果未结束,则再jon()这个地方一致阻塞,导致下面的代码不能被执行,如果在调用join()方法后,再调用该线程的wait()方法则会发生死锁,调用notify()则可能发生死锁或者join()失效。例子如下:
package soarhu;
import java.util.concurrent.TimeUnit;
class Service {
void readMethod() {
try {
for (int i = 0; i < 25; i++) {
TimeUnit.MILLISECONDS.sleep(300);
Thread.yield();
while (i==5){ //1 line
synchronized (this){
//wait(); // 发生死锁
this.notifyAll();
}
}
System.out.println(i + " " + Thread.currentThread().getName());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class Test {
public static void main(String[] args) {
Service o = new Service();
Thread t = new Thread() {
@Override
public void run() {
o.readMethod();
}
};
t.start();
try {
t.join();
for (int i = 0; i < 10; i++) {
System.out.println("main......." + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
输出结果:死锁
0 Thread-0
1 Thread-0
2 Thread-0
3 Thread-0
4 Thread-0
由于join()内部是用的wait()方法,那么也可能会抛出中断异常,举例如下:
package soarhu;
import java.util.concurrent.TimeUnit;
class Service extends Thread {
@Override
public void run() {
try {
for (int i = 0; i < 25; i++) {
TimeUnit.MILLISECONDS.sleep(300);
Thread.yield();
if (i==5){
this.interrupt();
}
System.out.println(i + " " + Thread.currentThread().getName());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class Test {
public static void main(String[] args) {
Service o = new Service();
o.start();
try {
o.join();
System.out.println("isAlive? "+o.isAlive());
for (int i = 0; i < 10; i++) {
System.out.println("main......." + i);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
输出结果:
0 Thread-0
1 Thread-0
2 Thread-0
3 Thread-0
4 Thread-0
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:340)
5 Thread-0
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
isAlive? false
at soarhu.Service.run(Test.java:12)
main.......0
main.......1
main.......2
main.......3
main.......4
main.......5
main.......6
main.......7
main.......8
main.......9 Process finished with exit code 0
示例:
package tij; /**
* Created by huaox on 2017/4/19.
*/ class Slepper extends Thread{
private int duration;
public Slepper(String name,int duration){
super(name);
this.duration = duration;
start();
} @Override
public void run() {
try {
sleep(duration);
}catch (InterruptedException e){
System.out.println(getName()+" was interrupted ");
return ;
}
System.out.println(getName()+" has awakened");
}
} class Joiner extends Thread{
private Slepper slepper;
public Joiner(String name,Slepper slepper){
super(name);
this.slepper = slepper;
start();
} @Override
public void run() {
try {
slepper.join();
}catch (InterruptedException e){
System.out.println(getName()+" was interrupted ");
return ;
}
System.out.println(getName()+" join completed");
}
} public class JoinTest {
public static void main(String[] args) {
Slepper slepper = new Slepper("slepper",1500);
Slepper grumpy = new Slepper("grumpy",1500);
Joiner dopey = new Joiner("dopyey->slepper",slepper);
Joiner doc = new Joiner("doc->grumpy",grumpy);
//grumpy.interrupt(); //line 1
} }
输出结果;
slepper has awakened
grumpy has awakened
dopyey->slepper join completed
doc->grumpy join completed Process finished with exit code 0
可以看到都是等到join()方法结束后才开使执行自身线程,如果把line 1的注释取消掉
输出结果:
grumpy was interrupted // 1
doc->grumpy join completed //2 这时,1和2一同结束 如果join()的所在线程发生异常,那么CurrentThread也将一同结束
slepper has awakened
dopyey->slepper join completed Process finished with exit code 0
此时
java多线程基本概述(七)——join()方法的更多相关文章
- Java 多线程基础(七)线程休眠 sleep
Java 多线程基础(七)线程休眠 sleep 一.线程休眠 sleep sleep() 方法定义在Thread.java中,是 static 修饰的静态方法.sleep() 的作用是让当前线程休眠, ...
- Java线程中yield与join方法的区别
长期以来,多线程问题颇为受到面试官的青睐.虽然我个人认为我们当中很少有人能真正获得机会开发复杂的多线程应用(在过去的七年中,我得到了一个机会),但是理解多线程对增加你的信心很有用.之前,我讨论了一个w ...
- Java多线程——<一>概述、定义任务
一.概述 为什么使用线程?从c开始,任何一门高级语言的默认执行顺序是“按照编写的代码的顺序执行”,日常开发过程中写的业务逻辑,但凡不涉及并发的,都是让一个任务顺序执行以确保得到想要的结果.但是,当你的 ...
- Java 多线程启动为什么调用 start() 方法而不是 run() 方法?
多线程在工作中多多少少会用到,我们知道启动多线程调用的是 start() 方法,而不是 run() 方法,你知道原因吗? 在探讨这个问题之前,我们先来了解一些多线程的基础知识~ 线程的状态 Java ...
- Java多线程【三种实现方法】
java多线程 并发与并行 并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行 并行:一组程 ...
- java多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么?
多线程有两种实现方法,分别是继承Thread类与实现Runnable接口 同步的实现方面有两种,分别是synchronized,wait与notify 先看一下java线程运行时各个阶段的运行状态 j ...
- java多线程基本概述(二十)——中断
线程中断我们已经直到可以使用 interrupt() 方法,但是你必须要持有 Thread 对象,但是新的并发库中似乎在避免直接对 Thread 对象的直接操作,尽量使用 Executor 来执行所有 ...
- Java多线程(八)——join()
一.join()介绍 join() 定义在Thread.java中.join() 的作用:让“主线程”等待“子线程”结束之后才能继续运行.这句话可能有点晦涩,我们还是通过例子去理解: // 主线程 p ...
- Java多线程学习(七)并发编程中一些问题
本节思维导图: 关注微信公众号:"Java面试通关手册" 回复"Java多线程"获取思维导图源文件和思维导图软件. 多线程就一定好吗?快吗?? 并发编程的目的就 ...
随机推荐
- 重新认识JavaScript
JavaScrpit在我眼中一直是web前端脚本语言,而这段时间的一些工作,让我对JavaScript有了一个全新的认识. 公司准备启动的一个手游项目,服务器端准备使用网易的开源框架pomelo.po ...
- APICloud使用
APICloud-APP开发平台 [网址:]http://www.apicloud.com/ APICloud studio 下载 打开网址,找到开发者社区->文档->下载->开发工 ...
- 关于利用input的file属性在页面添加图片的问题
在页面添加图片涉及到兼容的问题怎么解决兼容问题呢?请看下面分析: 在IE浏览器上面我们能直接通过获取其input的value值来获取其图片的路径. 在火狐和谷歌需要用createObjectURL(( ...
- 购买的wemall 6.0商城系统源码分享
使用方法 1.解压目录 2.cd wemall6 && npm i 3.配置config下的config.json 4.npm start 摒弃以往的开发框架thinkphp,使用no ...
- 3856: Monster
3856: Monster Time Limit: 1 Sec Memory Limit: 64 MBSubmit: 351 Solved: 161[Submit][Status][Discuss ...
- CLR查找和加载程序集的方式(二) 流程图
在前一篇文章<CLR查找和加载程序集的方式(一)>中详细介绍了CLR查找和加载程序的方式,分别介绍了配置与代码的实现方式. 本篇通过一个具体的流程图来帮助大家更加直观明了深入的掌握CLR查 ...
- PowerDesigner建模应用(一)逆向工程,配置数据源并导出PDM文件
物理数据模型(Physical Data Model)PDM,提供了系统初始设计所需要的基础元素,以及相关元素之间的关系:数据库的物理设计阶段必须在此基础上进行详细的后台设计,包括数据库的存储过程.操 ...
- Canvas的下雪效果
cfs.snow.js canvas 下雪场景 不会影响页面使用 使用方式非常简单 利用这个js文件,我们就能很快的让页面出现下雪的动画效果. 例如 <script type="tex ...
- Web 页面测试总结—控件类
web端页面测试,最常见的是基本控件的测试,只有了解常见的控件和其测试方法,才能掌握测试要点,避免漏测情况发生.根据日常工作总结,将控件和常见逻辑集合在一起,总结了几个控件类测试查场景如下. 导航条 ...
- entity framework core 支持批量插入,值得期待
entity framework6.x之前搞了这么多版本,构架这么牛B,居然没有批量插入更新的功能,但有很多替换的解决方案,例如Entity Framework Extended Library(ht ...