前言
有次偶尔接触到了Thread.join()
方法,便到网上去查阅相关资料,但也是看得我一头雾水(天下文章一般抄)。在我很久的理解之后,便想将我理解的join()
方法以一张图的方式解释出来。
要学习本篇文章,你需要理解以下知识:
仔细看
开局扔张图:
我绘制的这张图以时间线的形式解释了主线程和各个被实例化的John
线程的运行过程。
别着急,多看两遍!
我们可以看到,在主线程调用了join()
方法后,指定的线程会被抓回来在后边老老实实地排队。
猜一下
问:请回答未执行join()
方法和执行了join()
方法的运行结果有什么不同(可能有几种结果、运行的时间、输出的顺序)?
💡—!>
答:
类型|可能结果数量|运行速度|输出顺序|时间复杂度(最好)
---|---|---
不使用join()
|6种|快(多线程)|乱序|O(1)
使用join()
|1种|慢(单线程)|顺序|O(n)
Q&A
看完后,你可能还会有些疑问:
Q:为什么不使用join()
的可能结果数量是6种?输出顺序为什么是乱序?
A:由于多线程的原因,虽然start()
顺序执行了线程1/2/3,但可能由于各种原因,某个线程会抢先完成,从而造成了6种运行结果 ——
1123
2132
3213
4231
5321
6312
多线程并不老实,并且难以(不是不可能)控制。
Q:为什么使用join()
后会变慢?
A:因为使用join()
后,三个线程会按顺序被赶到主线程去运行,这时候它们就不能够同时运行了,只能一个一个地运行。
自己做
别偷懒,打开你的IDE,把下边的代码粘贴进去:
1public class TestJoin {
2 public static void main(String[] args) {
3 John john1 = new John();
4 John john2 = new John();
5 John john3 = new John();
6
7 try {
8 john1.start();
9 john1.join();
10 john2.start();
11 john2.join();
12 john3.start();
13 john3.join();
14 } catch (InterruptedException IE) {
15 IE.printStackTrace();
16 }
17 }
18}
19
20class John extends Thread {
21 @Override
22 public void run() {
23 for (int i = 0; i < 2; i++)
24 {
25 try
26 {
27 Thread.sleep(500);
28 System.out.println("Current Thread: "
29 + Thread.currentThread().getName());
30 }
31
32 catch(Exception ex)
33 {
34 System.out.println("Exception has" +
35 " been caught" + ex);
36 }
37 System.out.println(i);
38 }
39 }
40}
41
运行结果:
1Current Thread: Thread-0
20
3Current Thread: Thread-0
41
5Current Thread: Thread-1
60
7Current Thread: Thread-1
81
9Current Thread: Thread-2
100
11Current Thread: Thread-2
121
可以看到,苦逼的三个线程都被拉到了主线程顺序执行。
现在,删掉两行代码:
1john2.join();
2john3.join();
再次运行:
1Current Thread: Thread-0
20
3Current Thread: Thread-0
41
5Current Thread: Thread-1
60
7Current Thread: Thread-2
80
9Current Thread: Thread-2
101
11Current Thread: Thread-1
121
可以看到,john2
和john3
两个线程并未在主线程中运行,所以运行结果也发挥得比较自由,且运行时间也缩短了。
此处请注意,在使用join()
方法之前,一定要先使用start()
方法启动线程。线程根本没工作,那还咋拉过去?
后语
到此,你已经掌握了join()
方法的使用。不要问我能用来做什么,等到你需要它的功能时,你就不会再手忙脚乱了。
很认真,你读到了最后。让我们再来讲讲yield()
方法吧(别的文章都讲了):
yield()
方法的作用,与join()
方法无瓜。当你对一个线程执行yield()
方法后,该线程会尝试暂停自己,给后面正在排队的同级线程让道(即拱手让人),它是一个不稳定的过程(不一定有效果,也不一定什么时候继续执行)。
另外,建议你趁着还能再学进点什么,再看看syncronized
、wait()
、notify()
这些有相似之处的知识比较好。