前言
前面我们全面介绍了多线程间是如何通信的: 地址链接,接下来我们详细的介绍join方法,它有什么作用解决了什么问题。
1、引入
在多线程的环境中,如果要想实现这样一个效果:子线程的执行过程中比较耗费时间,但是我想 主线程在子线程执行完之后再执行,这时候有一个方法它可以解决此问题,那就是join方法
2、代码展示
2.1 创建一个线程MyThread
public class MyThread extends Thread {
@Override
public void run() {
try {
int secondValue = (int) (Math.random() * 10000);
System.out.println(secondValue);
Thread.sleep(secondValue);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2.2 主程序
public class Test {
public static void main(String[] args) {
try {
MyThread myThread = new MyThread();
myThread.start();
myThread.join();
System.out.println("当线程myThread执行完毕后再执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2.3 运行结果
2.4 分析
方法join的作用是使所属的线程对象x( 此代码中的myThread)正常执行run()方法中的任务,而使当前线序Z (此代码中的Main线)程进行无期限的阻塞就,等待线程x销毁后再继续执行线程。
2.5解读疑问——为什么join阻塞的是主(父)线程
这个问题困扰了我一会,且看join源码,其实它就是wait,wait的作用是释放对象锁,释放CUP资源,使当前线程阻塞,而当前线程就是main线程,当时让我疑惑是因为我把myThread当成了当前线程,其实myThread就是一个线程对象,是在主线程进行实例化的,所以main线程才是阻塞的对象,也就是join阻塞的是主线程。
public final void join() throws InterruptedException {
join(0);
}
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); //如果 timeout 为零,则不考虑实际时间,在获得通知前该线程将一直等待。
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
3 join(long)与sleep(long)的区别
结论先行: join释放锁,sleep不释放锁
分析:首先刚才上面咱们看了源码,join的底层是用wait实现的,wait释放锁,所以join也就是释放锁。sleep是不释放锁的,它一直使当前线程阻塞,直到时间到才会释放锁。
4 方法join()后面的代码提前运行的原因分析
前提:方法join()大部分是先运行的(java多线程核心编程技术一书中给的结论),就是说它先抢到线程锁,然后再快速进行释放,因为源码中wait(0)表示的事无限阻塞。
分析java(long)导致后面的代码提前运行:
部分代码:
public class Main {
public static void main(String[] args) throws InterruptedException {
ThreadB threadB=new ThreadB();
ThreadA threadA=new ThreadA(threadB);
threadA.start();
threadB.start();
threadB.join(2000);
System.out.println(" main end "+System.currentTimeMillis());
}
}
threadB.join() 先抢到threadB锁,然后快速释放,此时A抢到锁,执行完,此时threadB.join(2000)(代表主线程,上面解释过了)和线程threadB进行争抢锁,此时就会出现不同的结果,可能join(2000)先抢到线程,然后后边的代码就行了,这也就导致后面的代码提前运行。也可能threadB先抢到锁,执行完,再执行主线程后边的代码。