join() 是Thread的实例方法,官方解释为:等待该线程终止。
join() 方法的作用就是:将调用join的线程优先执行,当前正在执行的线程阻塞,直到调用join方法的线程执行完毕
或者被打断,主要用于线程之间的交互。
“Java 7 Concurrency Cookbook” 对 join() 方法的定义:
public final void join()throws InterruptedException: Waits for this thread to die.
thread.join() 方法阻塞调用此方法的线程(calling thread),直到线程thread完成,此线程再继续;通常用于在
main() 主线程内,等待其它线程完成再结束 main() 主线程。
另外还有一个应用场景:如何实现让线程T1,T2,T3,在T1执行完成后才执行T2,T2执行完成后才执行T3,
也就是线程的串行化,通过 Thread 类的 join() 方法就可以实现。
代码演示,主要是对比,使用 join()方法前后效果对比。
创建线程 Thread-a 代码:
/**
* @title: ThreadA
* @description: TODO
* @date 2019/6/6
*/
public class ThreadA implements Runnable{
private String threadName;
private Thread thread;
// 构造方法中就启用线程
ThreadA(String threadName) {
this.threadName = threadName;
thread = new Thread(this, threadName);
thread.start();
}
@Override
public void run() {
System.out.println("线程" + Thread.currentThread().getName() + "业务逻辑开始!");
for(int i = 6; i > 0 ; i--) {
// 线程睡眠1秒
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " : " + i);
} catch (Exception e) {
System.out.println(Thread.currentThread().getName() + "线程执行异常!");
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " 线程结束!");
}
public String getThreadName() {
return threadName;
}
public Thread getThread() {
return thread;
}
}
测试代码一:
/**
* @title: ThreadMain
* @description: TODO
* @date 2019/6/6
*/
public class ThreadMain {
public static void main(String[] args) throws InterruptedException {
// 创建线程 B
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
// 启动线程 Thread-a
ThreadA threadA = new ThreadA("Thread-a");
try {
// 加入 join 方法
threadA.getThread().join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 线程B的业务逻辑
System.out.println("开始线程 " + Thread.currentThread().getName() + "业务逻辑");
for (int i = 6; i > 0 ; i--) {
try {
System.out.println(Thread.currentThread().getName() + " : " + i);
// 睡眠0.5秒
Thread.sleep(500);
} catch (Exception e) {
System.out.println(Thread.currentThread().getName() + " 线程执行异常!");
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " 线程结束!");
}
}, "Thread-B");
// 启动线程B
threadB.start();
// 加入 join 方法
threadB.join();
// 主线程业务逻辑开始
System.out.println("主线程业务逻辑开始");
for (int i = 6; i > 0 ; i--) {
try {
System.out.println(Thread.currentThread().getName() + " : " + i);
// 睡眠0.3秒
Thread.sleep(300);
} catch (Exception e) {
System.out.println(Thread.currentThread().getName() + " 线程执行异常!");
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " 线程结束!");
}
}
测试代码二,仅将测试代码一中的,两次调用 join() 方法部分注释掉,其他部分均一样。
测试代码二:
/**
* @title: ThreadMain
* @description: TODO
* @date 2019/6/6
*/
public class ThreadMain {
public static void main(String[] args) throws InterruptedException {
// 创建线程 B
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
// 启动线程 Thread-a
ThreadA threadA = new ThreadA("Thread-a");
// try {
// // 加入 join 方法
// threadA.getThread().join();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// 线程B的业务逻辑
System.out.println("开始线程 " + Thread.currentThread().getName() + "业务逻辑");
for (int i = 6; i > 0 ; i--) {
try {
System.out.println(Thread.currentThread().getName() + " : " + i);
// 睡眠0.5秒
Thread.sleep(500);
} catch (Exception e) {
System.out.println(Thread.currentThread().getName() + " 线程执行异常!");
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " 线程结束!");
}
}, "Thread-B");
// 启动线程B
threadB.start();
// 加入 join 方法
// threadB.join();
// 主线程业务逻辑开始
System.out.println("主线程业务逻辑开始");
for (int i = 6; i > 0 ; i--) {
try {
System.out.println(Thread.currentThread().getName() + " : " + i);
// 睡眠0.3秒
Thread.sleep(300);
} catch (Exception e) {
System.out.println(Thread.currentThread().getName() + " 线程执行异常!");
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " 线程结束!");
}
}
测试代码一和测试代码二,唯一的差别就是:测试代码二将测试代码一中的,两次调用 join() 方法部分注释掉了。
代码中,在线程 Thread-B 中调用了线程 Thread-a ,在主线程 Main 中调用了线程 Thread-B。同时,Thread-a
线程的睡眠时间是 1 秒,Thread-B 线程的睡眠时间是 0.5 秒,Main 线程的睡眠时间是 0.3 秒。
当加入 join() 方法后:由于Thread-B 线程中调用 Thread-a,优先执行Thread-a 线程,当前正在执行的Thread-B
线程堵塞,直到Thread-a 线程执行完毕。同理,由于Main 主线程中调用 Thread-B线程,故优先执行Thread-B 线程,
当前正在执行的 Main 主线程堵塞,直到Thread-B 线程执行完毕。
总结:
join() 方法的作用就是:将调用join的线程优先执行,当前正在执行的线程阻塞,直到调用join方法的线程执行完毕
或者被打断,主要用于线程之间的交互.
深入源码了解一下join(),这里仅截取 Thread 类 的 join() 和 join(millis) 方法
package java.lang;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.security.AccessController;
import java.security.AccessControlContext;
import java.security.PrivilegedAction;
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.LockSupport;
import sun.nio.ch.Interruptible;
import sun.reflect.CallerSensitive;
import sun.reflect.Reflection;
import sun.security.util.SecurityConstants;
public
class Thread implements Runnable {
……
…… /** 这里仅截取 Thread 类 的 join() 和 join(millis) 方法 */
……
/**
* Waits for this thread to die.
*
* An invocation of this method behaves in exactly the same
* way as the invocation
*
*
* {@linkplain #join(long) join}{@code (0)}
*
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* interrupted status of the current thread is
* cleared when this exception is thrown.
*/
public final void join() throws InterruptedException {
join(0);
}
/**
* Waits at most {@code millis} milliseconds for this thread to
* die. A timeout of {@code 0} means to wait forever.
*
* This implementation uses a loop of {@code this.wait} calls
* conditioned on {@code this.isAlive}. As a thread terminates the
* {@code this.notifyAll} method is invoked. It is recommended that
* applications not use {@code wait}, {@code notify}, or
* {@code notifyAll} on {@code Thread} instances.
*
* @param millis
* the time to wait in milliseconds
*
* @throws IllegalArgumentException
* if the value of {@code millis} is negative
*
* @throws InterruptedException
* if any thread has interrupted the current thread. The
* interrupted status of the current thread is
* cleared when this exception is thrown.
*/
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) { // 无限期等待直到b线程结束
while (isAlive()) {
// wait操作,那必然有synchronized与之对应,
// 成员方法加了synchronized说明是synchronized(this)。
// 注意这个wait()方法是Object类中的方法
wait(0);
}
} else { // 等待固定时间,如果b没结束,那么就不等待了。
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
线程模块,计划用一个系列博文整理总结。上一篇博文: 《线程系列1:创建线程》
本文在源码理解部分,参考了博文: 【Java】Thread类中的join()方法原理. 非常感谢博主!!!