首先给出结论:
主线程和子线程之间没有谁先谁后结束这种关联,它们只是各自负责自己的线程任务,如果该线程的任务结束了,该线程自然会结束运行。
talk is cheap,show me the code and the result
1.先来一个好理解的:
下面这个例子中,由于子线程循环了100次,干的活多,走的慢,所以结束的晚。可以看到执行结果,主线程结束后,子线程稍等了一会才结束执行,并没有主线程需要等待子线程结束的情况。
主线程的任务:
1.thread.start 开启子线程
2.System.out.println(Thread.currentThread().getName()+“结束了 ,这是main干的活”); 输出语句
子线程的任务:
for (int i = 0; i < 100; i++) {
if (i==99){
System.out.println("我现在是99,这是子线程干的活");
}
} //循环100次,并输出语句
也就是说,谁先完成任务,谁就会先运行结束。
public class ThreadOne {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i==99){
System.out.println("我现在是99,这是子线程干的活");
}
}
}
});
thread.start();
System.out.println(Thread.currentThread().getName()+"结束了 ,这是main干的活");
}
}
输出结果:
main结束了 ,这是main干的活
我现在是99,这是子线程干的活
2.主线程通过方法调用开启子线程并执行任务
记得刚开始学这个的时候我也是迷惑的,为什么,主线程的方法,方法里的内容还没执行完,主线程就结束了呐,我是脸脸懵逼呀,其实,还是按照第一步中说的,看执行的任务,先上代码:
public class Outter {
public static void main(String[] args) throws InterruptedException {
Outter outter = new Outter();
int b = 10;
int test = outter.test(b);//这里调用了方法并且在方法中开启了子线程,test()方法还没执行结束,为啥主线程就走完了呐
System.out.println(test);
System.out.println("main 结束了");
}
public int test(final int b) {
final int a = 10;
new Thread(){
public void run() {
System.out.println(a);
System.out.println(b);
};
}.start();
return 5555;
}
}
多次运行后,执行结果如下:
那上面这个例子中,main线程的任务是什么,子线程的任务是什么:
主线程:
1.第一部分活(干活的意思)
Outter outter = new Outter();
int b = 10;
int test = outter.test(b); //调用test方法
System.out.println(test);
System.out.println("main 结束了");
2.第二部分活
.start()开启子线程
final int a = 10;
return 5555;
子线程:
System.out.println(a);
System.out.println(b);
之前一直迷惑的是,主线程调用了test()方法,为啥这个方法没结束main线程就结束了呐,其实是因为主线程只需要调用了这个方法,并且返回一个返回值,主线程的任务就结束了,至于主线程创建的子线程,去干别的事情还需要多少时间,主线程是不关心的。
举个通俗的例子:周末我在家中玩lol,正玩着突然饿了(我是主线程),喊我女朋友去搞点吃的(调用吃东西方法,吃东西方法内部,开启女朋友线程[子线程]),我接着打lol,发现打完了,女朋友还没做好饭哈哈(手动god head),我去倒头睡了。我只是调用了吃东西方法,喊一下(调用后就结束了),女朋友啥时候做好饭我不管。。。
3.可以控制让子线程结束后,主线程再结束吗。。。
上面举得例子太不真实啦哈哈,我都飘了,不过也是为了帮助理解。实际情况中,主线程可能要等待子线程执行完,返回对应的结果,取道结果后,接下来主线程继续执行或者结束。打完游戏后,我需要等女朋友把饭拿过来吃了后,我才能饱饱得睡觉哈,饭不过来,我不能睡觉,也就是子线程结束前主线程不能结束。
那有没有什么方法呐?肯定是有的哈,Java的Thread类提供了join方法。
如果需要主线程等待子线程结束,只需要在主线程的任务中,用子线程对象调用join()方法即可。
这里就不再赘述了,引用别人写的一篇文件哈,比较全面的几种方式:
线程从启动到执行完毕,一般会有一些耗时操作,并不能像一般的程序语句,就能立马结束。如下代码:
package com.xxx.async;public class WaitThreadDone { public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { try { System.out.println("thread "+Thread.currentThread().getName()+" start to run."); Thread.sleep(2000); System.out.println("thread " + Thread.currentThread().getName() + " done."); } catch (Exception e) { e.printStackTrace(); } } }); System.out.println("main start to run."); t1.start(); System.out.println("main done."); }}
这是一个典型的线程耗时操作,默认情况下,线程t1它不会立马结束,因此执行结果就是:
我们希望主线程开始之后,等待子线程运行结束,主线程继续执行。
考虑子线程是一个耗时的阻塞操作,我们需要通过程序让子线程运行结束再执行主线程。这里给出如下几种解决办法:
①、t1.join()
②、while(t1.isAlive())
③、while(Thread.activeCount()>1)
④、CountDownLatch
下面来看看具体的实现:
第一种办法:t1.start();后面接上t1.join()代码,如下:
第二种办法:判断子线程是否还存活
第三种办法:判断活跃的线程是否大于1
第四种办法:通过同步工具类CountDownLatch实现
这种办法需要CountDownLatch对象侵入线程run()方法的代码中。最终执行结果和前面几种解决办法一致。
CountDownLatch通过await()方法阻塞主线程,等待其他线程运行结束,通过countDown()方法来释放锁,主线程开始执行。
总结:在这四种解决办法中,一、二两种办法需要知道线程的名字,当有很多线程同时执行的时候,有时候,我们是无法直观的知道每个线程的名字,这两个办法用的很少见。第四种办法在多线程的情况下也可以使用,就是需要设置CountDownLatch latch = new CountDownLatch(n),指定需要等待的线程数。第三种办法无需知道线程的名字和线程的数量,使用起来很直观。