t.join() / t.join(long millis),当前线程里调用其它线程 t 的 join 方法,当前线程阻塞,但不释放对象锁,直到线程 t 执行完毕或者 millis 时间到,当前线程进入可运行状态。
t.join(); // 使调用线程 t 在此之前执行完毕
t.join(1000); // 等待 t 线程,等待时间是 1000 毫秒
JDK 源码:
public final void join() throws InterruptedException {
join(0);
}
// 成员方法加了 synchronized 说明是 synchronized(this)
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");
}
// 这里使用了 while 循环做判断,然后调用 wait 方法,所以说 join 方法的执行是完全通过 wait 方法实现的
// 等待时间为 0 的时候,就是无限等待,直到线程执行完了
if (millis == 0) {
// 如果当前线程还存活的话,就等待
while (isAlive()) {
// 调用该线程的 join 方法的线程拿到锁之后进行等待,直到线程执行结束
wait(0);
}
} else {
// 如果是等待的特定时间的话
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
从代码上看,如果线程被生成了,但还未被启动,调用它的 join() 方法是没有作用的,将直接继续向下执行
join 方法实现是通过 wait(Object 提供的方法)。当 main 线程调用 t.join() 之前,main 线程必须拥有线程对象 t 的锁,然后 main 线程调用 t.wait(time),直到等待时间 time 结束或者 t 线程执行完毕即 t.isAlive() == false
Example 1
public class JoinTest implements Runnable {
public static int a = 0;
public void run() {
for (int k = 0; k < 5; k++) {
a = a + 1;
}
}
public static void main(String[] args) throws Exception {
Runnable r = new JoinTest();
Thread t = new Thread(r);
t.start();
System.out.println(a);
}
}
程序的输出结果是 5 吗?答案是:有可能。其实你很难遇到输出 5 的时候,通常情况下都不是 5。为什么呢?当主线程 main 方法执行 System.out.println(a) 这条语句时,线程还没有真正开始运行,或许正在为它分配资源准备运行。因为为线程分配资源需要时间,而 main 方法执行完 t.start() 方法后继续往下执行 System.out.println(a),这时得到的结果是 a 还没有被改变的值 0 。怎样才能让输出结果为 5?其实很简单,join() 方法提供了这种功能。
public static void main(String[] args) throws Exception {
Runnable r = new JoinTest();
Thread t = new Thread(r);
t.start();
t.join(); // 加入 join()
System.out.println(a);
}
这个时候,程序输入结果始终为 5。
Example 2
class RunnableImpl implements Runnable {
public void run() {
try {
System.out.println("Begin sleep");
Thread.sleep(1000);
System.out.println("End sleep");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class JoinTest {
public static void main(String[] args) {
Thread t = new Thread(new RunnableImpl());
t.start();
try {
t.join(1000);
System.out.println("joinFinish");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果是:
Begin sleep
End sleep
joinFinish
当 main 线程调用 t.join 时,main 线程等待 t 线程,等待时间是 1000,如果 t 线程 Sleep 2000 呢
class RunnableImpl implements Runnable {
public void run() {
try {
System.out.println("Begin sleep");
Thread.sleep(2000); // 原来为 1000
System.out.println("End sleep");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果是:
Begin sleep
joinFinish
End sleep
也就是说 main 线程只等 1000 毫秒,不管 t 什么时候结束
Example 3
class CustomThread1 extends Thread {
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + " start.");
try {
for (int i = 0; i < 5; i++) {
System.out.println(threadName + " loop at " + i);
Thread.sleep(1000);
}
System.out.println(threadName + " end.");
} catch (Exception e) {
System.out.println("Exception from " + threadName + ".run");
}
}
}
class CustomThread extends Thread {
CustomThread1 t1;
public CustomThread(CustomThread1 t1) {
this.t1 = t1;
}
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + " start.");
try {
t1.join();
System.out.println(threadName + " end.");
} catch (Exception e) {
System.out.println("Exception from " + threadName + ".run");
}
}
}
public class JoinTestDemo {
public static void main(String[] args) {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + " start.");
CustomThread1 t1 = new CustomThread1();
CustomThread t = new CustomThread(t1);
try {
t1.start();
Thread.sleep(2000);
t.start();
t.join(); // 下面会将此处注释掉
} catch (Exception e) {
System.out.println("Exception from main");
}
System.out.println(threadName + " end!");
}
}
结果:
main start. // main 方法所在的线程起动,但没有马上结束,因为调用 t.join(),所以要等到 t 结束了,此线程才能向下执行
[CustomThread1] Thread start. // 线程 CustomThread1 起动
[CustomThread1] Thread loop at 0 // 线程 CustomThread1 执行
[CustomThread1] Thread loop at 1 // 线程 CustomThread1 执行
[CustomThread] Thread start. // 线程 CustomThread 起动,但没有马上结束,因为调用 t1.join(),所以要等到 t1 结束了,此线程才能向下执行
[CustomThread1] Thread loop at 2 // 线程 CustomThread1 继续执行
[CustomThread1] Thread loop at 3 // 线程 CustomThread1 继续执行
[CustomThread1] Thread loop at 4 // 线程 CustomThread1 继续执行
[CustomThread1] Thread end. // 线程 CustomThread1 结束了
[CustomThread] Thread end. // 线程 CustomThread 在 t1.join() 阻塞处起动,向下继续执行的结果
main end! // 线程 CustomThread 结束,此线程在 t.join() 阻塞处起动,向下继续执行的结果
将上例中的 join 注释掉后结果为:
main start. // main 方法所在的线程起动,但没有马上结束,这里并不是因为 join 方法,而是因为 Thread.sleep(2000)
[CustomThread1] Thread start. // 线程 CustomThread1 起动
[CustomThread1] Thread loop at 0 // 线程 CustomThread1 执行
[CustomThread1] Thread loop at 1 // 线程 CustomThread1 执行
main end! // Thread.sleep(2000) 结束,虽然在线程 CustomThread 执行了 t1.join(),但这并不会影响到其他线程(这里 main 方法所在的线程)
[CustomThread] Thread start. // 线程 CustomThread 起动,但没有马上结束,因为调用 t1.join(),所以要等到 t1 结束了,此线程才能向下执行。
[CustomThread1] Thread loop at 2 // 线程 CustomThread1 继续执行
[CustomThread1] Thread loop at 3 // 线程 CustomThread1 继续执行
[CustomThread1] Thread loop at 4 // 线程 CustomThread1 继续执行
[CustomThread1] Thread end. // 线程 CustomThread1 结束了
[CustomThread] Thread end. // 线程 CustomThread 在 t1.join() 阻塞处起动,向下继续执行的结果
Example 4
main 线程调用 t.join 时,必须能够拿到线程 t 对象的锁,如果拿不到它是无法 wait 的,Example 2 中的
t.join(1000) 说明了 main 线程只等待 1 秒,但如果在它等待之前,其他线程获取了 t 对象的锁,它等待时间可不止 1 秒了
public class ThreadJoinTest {
public static void main(String[] args) {
Thread t = new Thread(new RunnableImpl());
new ThreadTest(t).start();
t.start();
try {
t.join(1000); // main 线程等 1s
System.out.println("join Finish");
} catch (InterruptedException e) {
e.printStackTrace();
}
// System.out.println("join Finish");
}
}
class RunnableImpl implements Runnable {
@Override
public void run() {
try {
System.out.println("Thread Begin sleep " + System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("Thread End sleep " + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThreadTest extends Thread {
Thread thread;
public ThreadTest(Thread thread) {
this.thread = thread;
}
@Override
public void run() {
holdThreadLock();
}
public void holdThreadLock() {
//用当前的线程当做lock
synchronized (thread) {
System.out.println("ThreadTest getObjectLock " + System.currentTimeMillis());
try {
Thread.sleep(9 * 1000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
System.out.println("ThreadTest ReleaseObjectLock " + System.currentTimeMillis());
}
}
}
注意:Thread.sleep() 会使当前运行的线程交出执行权。Thread.sleep(0) 的作用,就是 “触发操作系统立刻重新进行一次 CPU 竞争”。
结果是:
ThreadTest getObjectLock 1519645264719
Thread Begin sleep 1519645264719
Thread End sleep 1519645266719
ThreadTest ReleaseObjectLock 1519645273720
join Finish
或
Thread Begin sleep 1519644950436
ThreadTest getObjectLock 1519644950436
Thread End sleep 1519644952437
ThreadTest ReleaseObjectLock 1519644959438
join Finish
在 main 方法中,通过 new ThreadTest(t).start() 实例化 ThreadTest 线程对象,它通过 synchronized (thread) ,获取线程对象 t 的锁,并 sleep(9000) 后释放,这就意味着,即使 main 方法 t.join(1000) 等待一秒钟,它也必须等待 ThreadTest 线程释放 t 锁后才能进入 wait 方法中。
注意:t.join(1000) 是让 main 线程等待 1000ms 或 t 死掉后执行,所以 t.join(1000) 和 sleep(9000) 是同时的,ThreadTest 的实际等待时间还是 9s