线程合并 (join)

Posted XeonYu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程合并 (join)相关的知识,希望对你有一定的参考价值。

上一篇:
线程休眠 (sleep)

线程合并(join)

将指定的线程加入到当前线程,当前线程会处于 WAITING 状态,直到引用的线程死亡(出现异常或者执行完毕),然后当前线程再继续执行。
先来看看join方法源码


可以看到,join调用了另外一个重载的方法,我们再看看重载的那个方法。

可以看到,join方法最终调用的是一个同步方法,接收一个毫秒值。

我们来简单用一用:

有以下代码

    public static void main(String[] args) {

        Thread thread = new Thread(() -> {
            for (int i = 0; i < 30; i++) {
                try {
                    TimeUnit.MILLISECONDS.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + i);
            }

        }, "work");
        thread.start();
        for (int i = 0; i < 30; i++) {
            try {
                TimeUnit.MILLISECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + i);
        }

    }

上面代码运行结果是work线程和main线程交替打印的,这个没啥好说的。

如果我们想先让work线程执行完毕,然后再执行主线程。可以这样写:

    public static void main(String[] args) {

        Thread thread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName() + i);
            }

        }, "work");
        thread.start();

        /*合并线程*/
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        /*work线程的状态*/
        System.out.println(thread.getName() + "的状态:" + thread.getState());

        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + i);
        }

    }

运行结果如下:

可以看到,work线程执行完毕,且状态是 TERMINATED 后,主线程才继续执行的。

这个时候你可能就有疑问了,这不就是单线程执行吗,完全提体现不出join的意义啊。

如果你有这个问题,那我只能说

下面我们来看一个Join的使用场景。

之前我写过一篇博客

高德逆地理编码接口返回数据格式不统一以及百度逆地理编码接口返回数据解析失败的踩坑记录

大体是我需要同时调高德和百度的逆地理编码接口,把两个接口返回的数据合并一下使用,当时用的是RxJava的zip操作符实现的,现在的话当然是用kotlin的协程更简洁了。

其实,用Join实现也是ok的,为了方便,这里就用 kotlin 写了

fun main() {
    /*模拟高德接口返回的结果*/
    var gaodeResult = ""
    /*模拟高德接口返回的结果*/
    var baiduResult = ""

    /*高德线程*/
    val gaoDeThread = Thread({
        println("${Thread.currentThread().name}开始调用...")
        TimeUnit.SECONDS.sleep(2)
        println("${Thread.currentThread().name}调用完毕...")

        gaodeResult = "gaode"

    }, "GaoDe")
    /*高德线程*/
    val baiduThread = Thread({
        println("${Thread.currentThread().name}开始调用...")
        TimeUnit.SECONDS.sleep(2)
        println("${Thread.currentThread().name}调用完毕...")
        baiduResult = "baidu"
    }, "BaiDu")

    /*启动两个线程*/
    gaoDeThread.start()
    baiduThread.start()


    /*合并两个线程*/
    gaoDeThread.join()
    baiduThread.join()

    /*主线程拿到的结果*/
    println("${Thread.currentThread().name}===>${gaodeResult}---${baiduResult}")
}

来看下运行结果:

可以看到,用Join其实也能实现之前的需求。
这里跟单线程执行的区别就能看出来了,在单线程中,是没办法做到并发请求的。

join(long millis)

将指定的线程加入到当前线程,当前线程会处于 WAITING 状态,指定线程占用cpu资源执行任务,持续时间为millis,持续时间完毕后,指定线程将不再占用cpu资源,而是跟当前线程去争夺cpu资源。

示例代码:

fun main() {

    val thread = Thread {

        for (i in 1..10) {
            TimeUnit.MILLISECONDS.sleep(100)
            println("${Thread.currentThread().name}===》${i}")
        }
    }
    thread.start()
    /*join 500毫秒*/
    thread.join(500)
    for (i in 1..10) {
        TimeUnit.MILLISECONDS.sleep(100)
        println("${Thread.currentThread().name}===》${i}")
    }
}

运行结果:

可以看到,前5次打印是Thread-0 独自打印,join时间过后,开始和main线程交替打印。

好了,join大概就是这些

下一篇:

线程同步之synchronized关键字


如果你觉得本文对你有帮助,麻烦动动手指顶一下,可以帮助到更多的开发者,如果文中有什么错误的地方,还望指正,转载请注明转自喻志强的博客 ,谢谢!

以上是关于线程合并 (join)的主要内容,如果未能解决你的问题,请参考以下文章

java多线程进阶Fork/Join任务拆分与合并

java多线程进阶Fork/Join任务拆分与合并

Fork/Join 型线程池与 Work-Stealing 算法

多线程高并发编程 -- Fork/Join源码分析

多线程高并发编程 -- Fork/Join源码分析

java多线程 -- ForkJoinPool 分支/ 合并框架 工作窃取