join 原理手撕JDK8 源码

join 原理手撕JDK8 源码

当一回标题党😃,希望内容对道友们有所帮助

原版现象

  1. 示例代码
// 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();
        }
    }
}
  1. 编译运行
# 编译代码
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;
        }
    }
}

模拟总结

  1. 模拟代码
// 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);
        }
    }
}
  1. 运行总结
# 编译代码
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() 都是必不可少的

再撕吧一下

  1. 查看一下调用原版的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 状态,但这都不是重点
*/
  1. 再看调用山寨版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 实例 - 获取线程状态

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值