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多线程"获取思维导图源文件和思维导图软件. 多线程就一定好吗?快吗?? 并发编程的目的就 ...
随机推荐
- 清空file文件域的方法
我们在实际应用中经常希望能把文件域给清空,比如使用change事件时,因为不清空再次选择同一文件时将不在触发change事件. 但是在IE中,由于安全设置的原因,是不允许更改文件域的值的,也就是不能使 ...
- 网络安全实验室 注入关通关writeup
URL:http://hackinglab.cn 注入关 [1] 最简单的SQL注入username = admin' or ''='password随便什么都可以直接可以登录 [2] 熟悉注入环境 ...
- 3555: [Ctsc2014]企鹅QQ
3555: [Ctsc2014]企鹅QQ Time Limit: 20 Sec Memory Limit: 256 MBSubmit: 696 Solved: 294[Submit][Status ...
- 剑指offer_(4)
重建二叉树: 题目描述 输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树.假设输入的前序遍历和中序遍历的结果中都不含重复的数字. 例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍 ...
- Java集合ArrayList源码解读
最近在回顾数据结构,想到JDK这样好的代码资源不利用有点可惜,这是第一篇,花了心思.篇幅有点长,希望想看的朋友认真看下去,提出宝贵的意见. :) 内部原理 ArrayList 的3个字段 priva ...
- @Autowired 注释对在哪里和如何完成自动连接提供了更多的细微的控制。
1.@Autowired 可以用在多个地方,在 setter 方法上,属性上 或者 带有多个参数的任意方法上. Setter 方法中的 @Autowired. 当 Spring遇到一个在 setter ...
- 2017-3-20 HTML 基础知识
HTML的定义:HTML是一门编程语言的名字:超文本标记语言(Hyper Text Mark-up Language ),就是超越了文字的范畴,除了文字还可以有图片.视频.音频.动画.特效.表格.链接 ...
- JVM中堆内存和栈内存的区别
Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间 ...
- box-shadow IE8兼容处理
根据canisue(http://caniuse.com/#search=box-shadow),box-shadow兼容性如下图所示: 测试代码: <!DOCTYPE html> < ...
- 零基础入门学习UI设计指南
第一步:认识设计启蒙必备知识 学习一项技能,尤其是已经有一定沉淀并在各行各业有广泛应用的技能,就一定要对它先有充分的认知.在开始正式学习前,你需要花足够的经历去了解和查阅它的起源.发展.应用.未来. ...