多线程编程之join方法的详解

1,128 阅读3分钟

小知识,大挑战!本文正在参与“   程序员必备小知识   ”创作活动

作者的其他平台:

| CSDN:blog.csdn.net/qq_4115394…

| 掘金:juejin.cn/user/651387…

| 知乎:www.zhihu.com/people/1024…

| GitHub:github.com/JiangXia-10…

| 公众号:1024笔记

本文大概3868字,读完共需10分钟

1 前言

在Java线程编程中, join()方法主要是让调用该方法的thread在完成run方法里面的部分后, 再执行join()方法后面的代码。

比如:

public class Demo08 {
    public static void main(String[] args) throws InterruptedException {
        Thread08 thread08 = new Thread08();
        thread08.start();
//        thread08.join();
        System.out.println("子线程执行完成以后再执行");
    }
}
class Thread08 extends Thread{
    @Override
    public void run() {
        try {
            int value = (int) (Math.random() * 10000);
            System.out.println("需要等待" + value + "毫秒");
            Thread.sleep(value);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

不使用join方法的结果如下:

图片

  使用了join方法之后的运行结果如下:

图片

输出第一句后等待9497毫秒,输出第二句。

join方法的作用就是使所属的线程对象A(子线程)执行run方法中的任务,而使得当前线程B(主线程)进行无限期的阻塞,等待线程A销毁后再继续 执行线程B后面的代码。方法join具有线程排队运行的作用,有些类似于同步代码运行的效果。

2 join的源码分析

从前面的例子可以发现,join具有使得线程排队的效果,就像同步一样。

但是join方法底层使用的是wait方法。

join的源码如下:

图片

可以发现这里调用了join(long millis)方法,进入join(long millis)方法如下:

图片

通过上面的方法可以发现当join(long millis)方法的参数是0时,运行这个方法的线程就会执行wait(0)。而wait方法的源码如下:

图片

可以发现其实wait()方法其实执行的就是wait(0)。所以主线程一直等待。

而通过join(long milis)的源码可以发现,这个方法使用synchronized关键字进行修饰的,也就是调用这个方法需要获得子线程对象的锁,然后调用wait,即先获得锁后释放锁,然后等待唤醒。

当线程运行结束的时候,notify是被线程的子系统调用的,进而主线程被唤醒!

join与synchroinzed区别:join的内部使用wait方法进行等待,而synchronized关键字使用的是对象锁的机制作为同步。

3 join方法的使用

join(long)方法使用:方法join(long)中的参数是设定等待的时间。比如:

public class Demo10 {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread10();
        t.start();
        t.join(2000);
        System.out.println("主线程结束于" + System.currentTimeMillis());
    }
}

class Thread10 extends Thread{
    @Override
    public void run() {
        try {
            System.out.println("子线程开始于" + System.currentTimeMillis());
            Thread.sleep(5000);
            System.out.println("子线程结束于" + System.currentTimeMillis());
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

结果如下:

图片

上面代码如果改成使用sleep(2000),运行的效果还是等待2000毫秒,和使用join(2000)运行效果没一样,但是它们在同步的处理上不一样,前面源码分析提到join底层是使用wait来实现所以会释放锁,而sleep不释放锁!所以joing(long)方法也具有释放同步锁的特点

比如:

public class Demo11 {
    public static void main(String[] args) {
        Thread11_A thread11_a = new Thread11_A();
        Thread11_B thread11_b = new Thread11_B(thread11_a);
        thread11_b.start();
        Thread11_C thread11_c = new Thread11_C(thread11_a);
        thread11_c.start();
    }
}

class Thread11_A extends Thread{
    @Override
    public void run() {
        try{
            System.out.println("线程A开始于:"+System.currentTimeMillis());
            Thread.sleep(5000);
            System.out.println("线程A结束于:"+System.currentTimeMillis());
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }

    synchronized public void foo(){
        System.out.println("方法执行时间" + System.currentTimeMillis());
    }
}


class Thread11_B extends Thread {
    private Thread11_A thread11_a;

    public Thread11_B(Thread11_A thread11_a) {
        this.thread11_a = thread11_a;
    }

    @Override
    public void run() {
        synchronized (thread11_a) {
            try {
                thread11_a.start();
//                Thread.sleep(6000);
                thread11_a.join();
                for (int i = 0; i < Integer.MAX_VALUE; i++) {
                    String s = new String();
                    Math.random();
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}

class Thread11_C extends Thread {
    private Thread11_A thread11_a;

    public Thread11_C(Thread11_A thread11_a) {
        this.thread11_a = thread11_a;
    }

    @Override
    public void run() {
        thread11_a.foo();
    }
}

结果如下:

图片

如果当join与interrupte方法如果彼此遇到,则会出现异常,但进程并没有结束,因为ThreadA还在继续运行,线程A并没有出现 异常,是正常状态下继续 执行。比如:

public class Demo09 {
    public static void main(String[] args) throws InterruptedException {
        Thread09_B t = new Thread09_B();
        t.start();
        Thread.sleep(10);
        Thread t2 = new Thread09_C(t);
        t2.start();
    }
}

class Thread09_A extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            // 耗时操作
            String s = new String();
            Math.random();
        }
    }
}

class Thread09_B extends Thread{
    @Override
    public void run() {
        try {
            Thread09_A t1 = new Thread09_A();
            t1.start();
            t1.join();
            System.out.println("B线程正常结束");
        } catch (InterruptedException e) {
            System.out.println("B线程异常结束");
            e.printStackTrace();
        }
    }
}

class Thread09_C extends Thread{
    private Thread09_B t;
    public Thread09_C(Thread09_B t){
        this.t = t;
    }

    @Override
    public void run() {
        t.interrupt();
    }
}

结果如下:

图片

相关推荐: