join 原理手撕JDK8 源码
当一回标题党😃,希望内容对道友们有所帮助
原版现象
- 示例代码
// TT01.java
public class TT01 {
public static void main(String []args) throws Exception {
Thread tt = new Thread(() -> {
try {
for (int i=0; i<5; i++) {
System.out.println("tt thread: " + i);
Thread.sleep(50);
}
} catch (Exception e) {
e.printStackTrace();
}
});
tt.start();
for (int i=0; i<10; i++) {
System.out.println("Main thread: " + i);
if (i==5) tt.join();
}
}
}
- 编译运行
# 编译代码
javac TT01.java
# 运行程序
java TT01
# 运行结果
tt thread: 0
Main thread: 0
Main thread: 1
Main thread: 2
Main thread: 3
Main thread: 4
Main thread: 5
tt thread: 1
tt thread: 2
tt thread: 3
tt thread: 4 # <---- 线程tt 结束
Main thread: 6 # <---- 线程Main 继续
Main thread: 7
Main thread: 8
Main thread: 9
## 在Main 调用tt.join() 之前(i != 5), 线程Main 与线程tt 并行
## 在Main 调用tt.join() 之后(i == 5), 先线程tt,完毕后再执行线程Main
## 从表面上看,仿佛join() 将tt 线程的运行内容并入了Main 线程一般
## 无论执行多少次,都会在"tt thread: 4"之后才打印"Main thread: 6"等内容
源码参考
// JDK8 中Thread 的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);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
模拟总结
- 模拟代码
// TT02.java
public class TT02 {
public static void main(String []args) throws Exception {
TT tt = new TT();
tt.setName("TT thread");
tt.start();
for (int i = 0; i < 10; i++) {
System.out.println("Main: " + i);
if (i == 5)
tt.dloop();
}
}
}
// 自定义线程类
class TT extends Thread {
@Override
public void run() {
try {
for (int i = 0; i < 5; i++) {
System.out.println(getName() + ": " + i);
sleep(50);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 模拟join() 的方法
public void dloop() throws InterruptedException {
// 本线程销毁前一直循环
while (this.isAlive()) {
sleep(5);
}
}
}
- 运行总结
# 编译代码
javac TT02.java
# 运行程序
java TT02
# 执行结果
TT thread: 0
Main: 0
Main: 1
Main: 2
Main: 3
Main: 4
Main: 5
TT thread: 1
TT thread: 2
TT thread: 3
TT thread: 4 # <---- 线程TT 结束
Main: 6 # <---- 线程Main 继续
Main: 7
Main: 8
Main: 9
## 模拟代码同样也达到了JDK8 中Thread 类join() 方法的效果,基本思路就是
## Thread 的子类对象提供一个方法,例如模拟代码里的dloop() 让Main 线程调用
## 将Main 线程带沟里,让其运行一个死循环,对应的线程不结束该循环就一直继续下去
## 所以,当我们看到Main 线程调用join() 后就阻塞了,实际上是Main 线程掉入了死循环里
## 从模拟代码也可以看出join() 的核心并不是synchronized 和wait() 来控制锁,而是那个死循环
## 当然为了让线程安全synchronized 和wait() 以及线程结束后调用的notifyAll() 都是必不可少的
再撕吧一下
- 查看一下调用原版的
join()
后线程的状态
package com.abc.java.juc;
public class JoinState {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
System.out.println("thread1 started ---------------------------------------");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打印当前线程组所有线程的状态
ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
int noThreads = threadGroup.activeCount();
Thread[] lstThreads = new Thread[noThreads];
noThreads = threadGroup.enumerate(lstThreads);
for (Thread thread : lstThreads) {
System.out.println("线程数量:" + noThreads + " 线程id:" + thread.getId() + " 线程名称:" + thread.getName() + " 线程状态:" + thread.getState());
}
System.out.println("thread1 finished ---------------------------------------");
}, "thread1");
System.out.println("thread1 created ---------------------------------------");
thread1.start();
System.out.println("main join thread1 ---------------------------------------");
thread1.join();
System.out.println("main finished ---------------------------------------");
}
}
/*
运行结果
thread1 created ---------------------------------------
main join thread1 ---------------------------------------
thread1 started ---------------------------------------
线程数量:2 线程id:1 线程名称:main 线程状态:WAITING <--
线程数量:2 线程id:9 线程名称:thread1 线程状态:RUNNABLE
thread1 finished ---------------------------------------
main finished ---------------------------------------
源码中join() 里有wait(0); 即this.wait(0); 也就是将该线程作为同步监视器(俗称锁)
所以,main 线程调用了join() --> this.wait(0); --> WAITING 状态,但这都不是重点
*/
- 再看调用山寨版
join()
即dloop()
后线程的状态
package com.abc.java.juc;
public class JoinTest {
public static void main(String[] args) throws InterruptedException {
System.out.println("Main started! ---------------------------------------");
TT tt = new TT();
tt.setName("thread1");
tt.start();
System.out.println("Main join/dloop thread1 -----------------------------");
tt.dloop();
for (int i = 1; i < 5; i++) {
System.out.println("Main runing " + i);
Thread.sleep(300);
}
System.out.println("Main finished! ---------------------------------------");
}
}
class TT extends Thread {
@Override
public void run() {
System.out.println("thread1 started ---------------------------------------");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打印当前线程组所有线程的状态
ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
int noThreads = threadGroup.activeCount();
Thread[] lstThreads = new Thread[noThreads];
noThreads = threadGroup.enumerate(lstThreads);
for (Thread thread : lstThreads) {
System.out.println("线程数量:" + noThreads + " 线程id:" + thread.getId() + " 线程名称:" + thread.getName() + " 线程状态:" + thread.getState());
}
System.out.println("thread1 finished ---------------------------------------");
}
//public synchronized void dloop() throws InterruptedException {
public void dloop() throws InterruptedException {
while (true) {
//sleep(50);
if (!this.isAlive()) break;
//this.wait();
//System.out.println("***************************");
}
}
}
/*
运行结果
Main started! ---------------------------------------
Main join/dloop thread1 -----------------------------
thread1 started ---------------------------------------
线程数量:2 线程id:1 线程名称:main 线程状态:RUNNABLE <---
线程数量:2 线程id:9 线程名称:thread1 线程状态:RUNNABLE <---
thread1 finished ---------------------------------------
Main runing 1
Main runing 2
Main runing 3
Main runing 4
Main finished! ---------------------------------------
可见,Main 线程和thread1 线程都处于RUNNABLE 状态,也同样实现了join() 的效果
所以,印证了前面说的`死循环把Main 线程带沟里`才是join() 的核心代码,而锁保障的是线程安全
*/
参考资料
Java 浅析 Thread.join()
Thread之三:Thread Join()的用法
Java并发-ThreadGroup获取所有线程
Java 实例 - 获取所有线程
Java 实例 - 获取线程状态