并发编程学习之join()方法

线程之间的协作就跟人与人之间的协作一样,有时候我们需要等待别人完成某项任务后,我们再来开始工作。有时候会出现一个线程的输入非常依赖另一个或多个线程的输出,此时这个线程就需要等待依赖线程执行完毕才能继续执行。在Thread类中有join()方法具有这样的功能:

第三个方法是无限等待,他会一直阻塞当前线程,直到目标线程执行完毕。前两个方法就定义了一个最大等待时间,时间过了,当前线程“等不及了”会继续往下执行。要注意的是join()的本质是让调用线程wait()在当前线程对象的实例上(具体wait()方法的介绍可以参看:https://blog.csdn.net/Dongguabai/article/details/82230279)。join()方法的源码如下:

这里有一个代码示例:

package dgb.test.concurrent;

import lombok.extern.slf4j.Slf4j;

/**
 * @author Dongguabai
 * @date 2018/8/31 15:17
 */
@Slf4j
public class JoinTest {

    private static volatile int i = 0;

    static class Thread1 extends Thread{
        @Override
        public void run() {
            log.info("Thread1线程开始运行!!!");
            for (i=0;i<10000000;i++);
            log.info("Thread1线程运行结束!!!");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        log.info("主线程开启-------------");
        Thread1 t1 = new Thread1();
        t1.start();
        t1.join();
        log.info("主线程结束-------------{}",i);
    }
}

运行结果:

在运行结果的最后输出的i是10000000,证明了join()方法的效果,因为假如这里没有join()方法,在main()中输出的i应该为0或者特别小。

再简单分析一下main()中代码的执行流程:

有一个关键的地方就是thread1.wait()后,主线程会进入了thread1的等待队列中,在thread1执行完成后,会调用thread1.notify()方法,释放所有等待线程继续执行:

因此建议在程序中不要在Thread对象实例上使用类似wait()或者notify()等方法,因为这个很有可能影响系统API的工作或者被系统的API锁影响。

再来看一个例子,就很好理解了:

public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("thread1执行---");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("thread1结束---");
            }
        },"threard1");
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("thread2执行---");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("thread2结束---");
            }
        },"threard2");
        Thread thread3 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("thread3执行---");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("thread3结束---");
            }
        },"threard3");
        thread1.start();
        thread1.join();
        thread2.start();
        thread2.join();
        thread3.start();
    }

执行结果:

再看一个例子:

package com.example.threaddesign;

import java.util.stream.IntStream;

/**
 * @author Dongguabai
 * @date 2018/12/4 14:17
 */
public class ThreadJoin {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            IntStream.range(1,10).forEach(i-> System.out.println(Thread.currentThread().getName()+"->"+i));},"t1");
        Thread t2 = new Thread(()->{
            IntStream.range(1,10).forEach(i-> System.out.println(Thread.currentThread().getName()+"->"+i));},"t2");
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("main 线程开始了----");
        IntStream.range(1,10).forEach(i-> System.out.println(Thread.currentThread().getName()+"->"+i));
    }
}

要注意的是这个例子中,t1 和 t2 线程还是交替执行的,因为是 mian 线程去等待 t1 和 t2 执行完成。

运行结果:

t1->1
t2->1
t1->2
t2->2
t1->3
t2->3
t1->4
t2->4
t1->5
t2->5
t1->6
t2->6
t1->7
t2->7
t1->8
t2->8
t1->9
t2->9
main 线程开始了----
main->1
main->2
main->3
main->4
main->5
main->6
main->7
main->8
main->9

Process finished with exit code 0

再比如经常会有这样的一个面试题,需要在几个线程结束后计算这几个线程执行的时间,这样使用 join() 就很方便了,只需要保证工作线程 join() 在计时线程上就行了。

package com.example.threaddesign;

/**
 * @author Dongguabai
 * @date 2018/12/4 14:40
 */
public class ThreadJoin3 {

    public static void main(String[] args) throws InterruptedException {
        System.out.println("计时线程开始干活了。。。");
        long startTime = System.currentTimeMillis();

        Thread t1 = new Thread(new Task(1000));
        Thread t2 = new Thread(new Task(1500));
        Thread t3 = new Thread(new Task(2000));

        t1.start();
        t2.start();
        t3.start();

        t1.join();
        t2.join();
        t3.join();

        long endTime = System.currentTimeMillis();
        System.out.printf("全部线程工作结束,总共耗时::[%s]",endTime-startTime);
    }
}

class Task implements Runnable{

    //工作耗时
    private long time;

    public Task(long time) {
        this.time = time;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"  开始干活了");
        try {
            Thread.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"  干活结束");
    }
}

输出结果:

计时线程开始干活了。。。
Thread-0  开始干活了
Thread-1  开始干活了
Thread-2  开始干活了
Thread-0  干活结束
Thread-1  干活结束
Thread-2  干活结束
全部线程工作结束,总共耗时::[2006]

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值