我们直接结合代码来讲解,
创建一个SimpleTestDemo.java,在里面分别调用三个子线程(三种编写方式,其实都一样):
public class SimpleTestDemo { public static void main(String[] args) throws InterruptedException { Thread t1= new Thread(new Runnable() { @Override public void run() { try { System.out.println("1111111111111 Thread"); } catch (Exception e) { e.printStackTrace(); } } }); //运行子线程 1 t1.start(); Thread t2=new Thread(() -> { try { System.out.println("2222222222222 Thread"); } catch (Exception e) { e.printStackTrace(); } }); //运行子线程 2 t2.start(); Thread t3= new Thread(() -> System.out.println("3333333333333 Thread")); //运行子线程 3 t3.start(); //主线程的输出 System.out.println(" The End !"); } }
运行结果:
从运行结果,可以看到,三个子线程异步执行,所以导致每次运行的输出结果不一定是一样的,因为都在抢资源。
为了让这种异步执行的场景模拟更加真实,我们在子线程1上加上sleep(2000):
再看看运行结果,因为子线程1执行时间久,所以最后才执行完毕:
OK,接下来就是Thread.join()的运用了。
虽然子线程1执行得久,但是我们依然想先确保子线程1被执行完毕后才执行后面的代码,那么我们就需要调用子线程1的join()方法:
这样我们再看看运行结果,尽管子线程1执行了很久,但是其他的代码依然等待子线程1执行完毕再进行执行:
到这里,很明显已经看到了join的作用了,简单来说,就是主线程跟各子线程之间其实都是异步的执行的;
但是如果使用了某个子线程的join()方法,那么就是 强行在此刻转变成了同步执行,也就是后面还未执行的代码(子线程调用等),都必须等,等到使用join()方法的线程执行完毕才能继续放飞自我。
那么join方法还有其他作用么, 有的,在调用的时候发现是可以填入参数的:
这个参数,毫秒,作用是什么呢?
作用是最大等待时间,也就是说虽然使用了join(),后面的代码需要等待这个子线程执行完毕后才能开始运行,但是我们如果想实现,如果这子线程执行时间超过某个限制,我们就不等了,直接执行后面的代码,这么我们只需要把限制时间毫秒级别作为参数填入join()即可。
之前我们子线程1 sleep(2000),那么我们就尝试下join(1000),也就是说,超过1000毫秒,就不等了:
看看运行结果,虽然子线程1使用了join,但是我们加上了最大等待时间1000毫秒,所以等了1000毫秒后,后面的代码直接运行了,最后最后子线程1才执行完毕输出了内容:
OK,到此