在很多情况下,主线程创建并启动子线程,如果子线程中有大量的耗时运算,主线程将早于子线程结束,如果想让主线程等待子线程结束后再结束,那么我们可以使用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()方法的更多相关文章

  1. Java 多线程基础(七)线程休眠 sleep

    Java 多线程基础(七)线程休眠 sleep 一.线程休眠 sleep sleep() 方法定义在Thread.java中,是 static 修饰的静态方法.sleep() 的作用是让当前线程休眠, ...

  2. Java线程中yield与join方法的区别

    长期以来,多线程问题颇为受到面试官的青睐.虽然我个人认为我们当中很少有人能真正获得机会开发复杂的多线程应用(在过去的七年中,我得到了一个机会),但是理解多线程对增加你的信心很有用.之前,我讨论了一个w ...

  3. Java多线程——<一>概述、定义任务

    一.概述 为什么使用线程?从c开始,任何一门高级语言的默认执行顺序是“按照编写的代码的顺序执行”,日常开发过程中写的业务逻辑,但凡不涉及并发的,都是让一个任务顺序执行以确保得到想要的结果.但是,当你的 ...

  4. Java 多线程启动为什么调用 start() 方法而不是 run() 方法?

    多线程在工作中多多少少会用到,我们知道启动多线程调用的是 start() 方法,而不是 run() 方法,你知道原因吗? 在探讨这个问题之前,我们先来了解一些多线程的基础知识~ 线程的状态 Java ...

  5. Java多线程【三种实现方法】

    java多线程 并发与并行 并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行 并行:一组程 ...

  6. java多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么?

    多线程有两种实现方法,分别是继承Thread类与实现Runnable接口 同步的实现方面有两种,分别是synchronized,wait与notify 先看一下java线程运行时各个阶段的运行状态 j ...

  7. java多线程基本概述(二十)——中断

    线程中断我们已经直到可以使用 interrupt() 方法,但是你必须要持有 Thread 对象,但是新的并发库中似乎在避免直接对 Thread 对象的直接操作,尽量使用 Executor 来执行所有 ...

  8. Java多线程(八)——join()

    一.join()介绍 join() 定义在Thread.java中.join() 的作用:让“主线程”等待“子线程”结束之后才能继续运行.这句话可能有点晦涩,我们还是通过例子去理解: // 主线程 p ...

  9. Java多线程学习(七)并发编程中一些问题

    本节思维导图: 关注微信公众号:"Java面试通关手册" 回复"Java多线程"获取思维导图源文件和思维导图软件. 多线程就一定好吗?快吗?? 并发编程的目的就 ...

随机推荐

  1. 极光推送助推视频App,打造最活跃手机新媒体平台

    移动应用能够帮助吸引更多的新用户,增加用户互动和对话.但你得让用户想起你,如何在一部手机上数十个App中脱颖而出,是考验App运营的关键之处.为了打造一个成功的App,开发者需要着眼长远,不应局限于其 ...

  2. 自学html5要花多长时间

    自学html5培训课程对于没有基础的那么就需要从头开始学,主要包括以下几方面 1. PC端网页制作基础,包括css,html 2. Javascript.jquery 主要是写一些动效 3. 学习UI ...

  3. Visual Studio 2017离线安装包,百度云分流

    Visual Studio正式版发布了,然而只能在线安装.虽然官方有提供了离线的方法,但还是蛮复杂的,所以我打包了两个版本发布至百度云分享. 离线分流 地址:http://pan.baidu.com/ ...

  4. TP框架数据库操作(增删改)

    首先选择一张表,对其进行操作: 对数据库操作之前首先要创建模型: $n = M("account"); 数据库添加数据: 1.使用数组: 1.使用数组 $arr = array(& ...

  5. iOS下的界面布局利器-MyLayout布局框架

      Swift:TangramKit: https://github.com/youngsoft/TangramKit OC:MyLayout: https://github.com/youngsof ...

  6. wemall app商城源码中基于JAVA的绑定和处理fragments和viewpager之间的逻辑关系代码

    wemall doraemon是Android客户端程序,服务端采用wemall微信商城,不对原商城做任何修改,只需要在原商城目录下上传接口文件即可完成服务端的配置,客户端可随意定制修改.本文分享其中 ...

  7. 3410: [Usaco2009 Dec]Selfish Grazing 自私的食草者

    3410: [Usaco2009 Dec]Selfish Grazing 自私的食草者 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 71  Solve ...

  8. 1707: [Usaco2007 Nov]tanning分配防晒霜

    1707: [Usaco2007 Nov]tanning分配防晒霜 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 548  Solved: 262[Sub ...

  9. TypeScript设计模式之解释器

    看看用TypeScript怎样实现常见的设计模式,顺便复习一下. 学模式最重要的不是记UML,而是知道什么模式可以解决什么样的问题,在做项目时碰到问题可以想到用哪个模式可以解决,UML忘了可以查,思想 ...

  10. [SQL] SQL 基础知识梳理(六)- 函数、谓词、CASE 表达式

    SQL 基础知识梳理(六)-  函数.谓词.CASE 表达式 目录 函数 谓词 CASE 表达式 一.函数 1.函数:输入某一值得到相应输出结果的功能,输入值称为“参数”,输出值称为“返回值”. 2. ...