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多线程"获取思维导图源文件和思维导图软件. 多线程就一定好吗?快吗?? 并发编程的目的就 ...
随机推荐
- Window下JDK安装与配置
今天项目组开会,由于.Net平台的限制无法满足现有业务需求,项目计划从.Net平台转Java平台,采用Java+Spark+Hadoop,之前关于Java和Hadoop的书也买的有只是平时看的少,最近 ...
- HTML5之通信和多线程
HTML通信 跨文档消息传输 HTML5提供了在网页文档之间相互接收和发送信息的功能,使用这个功能,只要获取到网页所在窗口对象的实例,不仅同源(域+端口)的网页可以相互通信,甚至可以实现跨域通信.涉及 ...
- 雪花降落CADisplayLink
p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 18.0px "PingFang SC"; color: #1d9421 } p.p2 ...
- 获取手机 id 与 ip
//id #import <AdSupport/AdSupport.h> //ip #include <ifaddrs.h> #include <arpa/inet.h& ...
- Android中的WebView实战详解(一)
一.为什么要用WebView? 1.兼容已有的项目2.可动态更新 二.WebView怎样使用? WebView是一个控件,如在布局中设置: <WebView android:id="@ ...
- Docker基础入门及示例
Docker近几年的发展可谓一日千里,特别从是2013年随着一个基于LXC的高级容器引擎开源,到现在,其在linux和windows上都有了很好的支持,并且已经有很多公司将docker用于实际的生产环 ...
- java之JDK的环境变量配置
JDK是什么? JDK是整个java开发的核心,它包含了JAVA的运行环境,JAVA工具和JAVA基础的类库. JDK包含的基本组件包括 java –--------> 运行编译后的java程序 ...
- JavaScript重新介绍
本文转载自 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/A_re-introduction_to_JavaScript 引言 为什么 ...
- servlet中的过滤器 国际化
1. 过滤器 基本概念 过滤器是需要在xml中配置的. 为什么需用到过滤器? 项目开发中,经常会涉及到重复代码的实现! 注册 ----à Servlet [1. 设置编码] ----à JSP 修改 ...
- 利用python的爬虫技术爬去糗事百科的段子
初次学习爬虫技术,在知乎上看了如何爬去糗事百科的段子,于是打算自己也做一个. 实现目标:1,爬取到糗事百科的段子 2,实现每次爬去一个段子,每按一次回车爬取到下一页 技术实现:基于python的实现, ...