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多线程"获取思维导图源文件和思维导图软件. 多线程就一定好吗?快吗?? 并发编程的目的就 ...
随机推荐
- Python学习路线图
文章转载自「开发者圆桌」一个关于开发者入门.进阶.踩坑的微信公众号 Python学习路线图你可以通过百度云盘下载观看对应的视频 链接: http://pan.baidu.com/s/1c2zLllA ...
- redux:applyMiddleware源码解读
前言: 笔者之前也有一篇关于applyMiddleware的总结.是applyMiddleware的浅析. 现在阅读了一下redux的源码.下面说说我的理解. 概要源码: step 1: apply ...
- Servlet中过滤器的执行流程
- 1029: [JSOI2007]建筑抢修
1029: [JSOI2007]建筑抢修 Time Limit: 4 Sec Memory Limit: 162 MBSubmit: 2382 Solved: 1033[Submit][Statu ...
- Spring Boot启动过程(五):Springboot内嵌Tomcat对象的start
标题和上一篇很像,所以特别强调一下,这个是Tomcat对象的. 从TomcatEmbeddedServletContainer的this.tomcat.start()开始,主要是利用Lifecycle ...
- ImportError: No module named cv2 解决方法
ImportError: No module named cv2 解决方法 import cv2时会出现这个问题 解决方法:将openCV安装目录里的python文件夹内的cv2.pyd复制到Py ...
- Android 快速开发系列 ORMLite 框架最佳实践
比较靠谱的Helper的写法: 1.DatabaseHelper package com.zhy.zhy_ormlite.db; import java.sql.SQLException; impor ...
- Omi框架学习之旅 - Hello World 及原理说明
学什么东西都从hello world开始, 我也不知道为啥. 恩,先上demo代码, 然后提出问题, 之后解答问题, 最后源码说明. hello world - demo: class Hello e ...
- background-size IE8兼容方案
根据canius(http://caniuse.com/#search=background-size),background-size兼容性为IE9以及以上浏览器,如下图所示. 实例代码: < ...
- 集合框架(HashSet存储自定义对象保证元素唯一性)
HashSet如何保证元素唯一性的原理 1.HashSet原理 a. 我们使用Set集合都是需要去掉重复元素的, 如果在存储的时候逐个equals()比较, 效率较低,哈希算法提高了去重复的效率, 降 ...