首先给出结论:
主线程和子线程之间没有谁先谁后结束这种关联,它们只是各自负责自己的线程任务,如果该线程的任务结束了,该线程自然会结束运行。

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;
    }
}

多次运行后,执行结果如下:

java 主线程和子线程通信 java主线程等待子线程结束_主线程


那上面这个例子中,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),我去倒头睡了。我只是调用了吃东西方法,喊一下(调用后就结束了),女朋友啥时候做好饭我不管。。。

java 主线程和子线程通信 java主线程等待子线程结束_主线程_02

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它不会立马结束,因此执行结果就是:

java 主线程和子线程通信 java主线程等待子线程结束_主线程_03

我们希望主线程开始之后,等待子线程运行结束,主线程继续执行。

java 主线程和子线程通信 java主线程等待子线程结束_子线程_04

考虑子线程是一个耗时的阻塞操作,我们需要通过程序让子线程运行结束再执行主线程。这里给出如下几种解决办法:

①、t1.join()
②、while(t1.isAlive())
③、while(Thread.activeCount()>1)
④、CountDownLatch

下面来看看具体的实现:

第一种办法:t1.start();后面接上t1.join()代码,如下:

java 主线程和子线程通信 java主线程等待子线程结束_主线程_05

第二种办法:判断子线程是否还存活

java 主线程和子线程通信 java主线程等待子线程结束_java 主线程子线程 线程池_06

第三种办法:判断活跃的线程是否大于1

java 主线程和子线程通信 java主线程等待子线程结束_子线程_07

第四种办法:通过同步工具类CountDownLatch实现

java 主线程和子线程通信 java主线程等待子线程结束_java 主线程子线程 线程池_08

这种办法需要CountDownLatch对象侵入线程run()方法的代码中。最终执行结果和前面几种解决办法一致。

CountDownLatch通过await()方法阻塞主线程,等待其他线程运行结束,通过countDown()方法来释放锁,主线程开始执行。

总结:在这四种解决办法中,一、二两种办法需要知道线程的名字,当有很多线程同时执行的时候,有时候,我们是无法直观的知道每个线程的名字,这两个办法用的很少见。第四种办法在多线程的情况下也可以使用,就是需要设置CountDownLatch latch = new CountDownLatch(n),指定需要等待的线程数。第三种办法无需知道线程的名字和线程的数量,使用起来很直观。