Thread.join()会使什么线程处于等待状态?

今天天气格外好,天空湛蓝,白云飘逸,阳光明媚,张三准备出门游玩,掏出手机打了个滴滴,一看需要等待3分钟,那就等等好了,谁叫司机离他有点远呢?3分钟后,司机达到,于是乎,上车出发,完美。

Thread.join()会使什么线程处于等待状态?_第1张图片

在这个例子中,将张三类比为主线程,打车后,主线程的操作已经完成,滴滴师傅需要3分钟才能到达,这个类比为子线程,主线程需要等到子线程执行完成后,才能继续往下执行。

那么,怎样才能满足这样的需求呢?

1.为什么需要join()方法

为了解决这样的需求,Thread类中为我们提供了join()方法,可以实现这种效果。

以下是通过来实现上述场景。

1)未调用join()方法。

import lombok.SneakyThrows;

import java.util.concurrent.TimeUnit;

public class ThreadJoinTest {
    @SneakyThrows
    public static void main(String[] args) {
        CustomRunnable customRunnable = new CustomRunnable();
        customRunnable.setCustomer("张三");
        Thread thread = new Thread(customRunnable);
        thread.start();
        // thread.join();
        System.out.println("滴滴师傅已到达,现在出发!");
    }

    static class CustomRunnable implements Runnable {

        private String customer;

        @SneakyThrows
        @Override
        public void run() {
            System.out.println(customer + "刚刚下单了,距离1km,预计3分钟到达!");
            TimeUnit.MINUTES.sleep(3);
        }

        public void setCustomer(String customer) {
            this.customer = customer;
        }
    }
}

执行结果:

当我们为调用join()方法时,主线程因为执行快,提前执行完成,而子线程需要3分钟才能执行完成,因此未能达到我们想要的效果。

2)调用join()方法

import lombok.SneakyThrows;

import java.util.concurrent.TimeUnit;

public class ThreadJoinTest {
    @SneakyThrows
    public static void main(String[] args) {
        CustomRunnable customRunnable = new CustomRunnable();
        customRunnable.setCustomer("张三");
        Thread threadA = new Thread(customRunnable);
        threadA.start();
        threadA.join();
        System.out.println("滴滴师傅已到达,现在出发!");
    }

    static class CustomRunnable implements Runnable {

        private String customer;

        @SneakyThrows
        @Override
        public void run() {
            System.out.println(customer + "刚刚下单了,距离1km,预计3分钟到达!");
            TimeUnit.MINUTES.sleep(3);
        }

        public void setCustomer(String customer) {
            this.customer = customer;
        }
    }
}

这个时候,由于子线程需要执行3分钟。

Thread.join()会使什么线程处于等待状态?_第2张图片

我们结合jpsjstack命令来看看main线程的状态,main线程处于State.WATING状态。

Thread.join()会使什么线程处于等待状态?_第3张图片

执行结果:

Thread.join()会使什么线程处于等待状态?_第4张图片

借助join()方法,已经达到我们想要的效果。

2.join()方法底层实现

我们先来看看底层实现代码:

// 其注释为等待此线程死亡
public final void join() throws InterruptedException {
    join(0);
}

继续跟踪,可以发现这里有几个步骤:

1)执行isAlive()方法,判断子线程存活状态。

由于我们的millis = 0,所以先执行isAlive()方法,意思是判断调用者所指代的线程是否处于活动状态,因为我们使用的是threadA对象调用join()方法,因此这里的此线程指代threadA所代表的线程,也就是子线程。

2)子线程存活,则暂停主线程

子线程存活,则执行wait(0)方法,wait()方法的目的是使当前线程处于State.WATING状态。因为我们是在主线程中调用的join()方法,所以当前线程一定是主线程。

这里容易产生疑惑的点就是,我们使用的是threadA对象调用的join()方法呀,为什么当前线程却指代的是主线程,其实threadA只能算作调用者。

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;
        }
    }
}

isAlive()方法,判断调用者所指代的线程是否处于存活状态。

public final native boolean isAlive();

wait()方法,使当前线程处于State.WATING状态,调用者处于哪个线程,该线程就是当前线程。

public final native void wait(long timeoutMillis) throws InterruptedException;

从上述代码,我们可以得知,join()方法的底层使用wait()方法来实现。使用wait()方法的前提是,其处于同步方法或同步代码块环境中,果不其然join()方法使用了synchronized关键字修饰,我们知道synchronized修饰非静态方法,锁对象是当前对象this,也就是调用者对象threadA。我们来验证一下。

public class ThreadJoinTest {
    @SneakyThrows
    public static void main(String[] args) {
        CustomRunnable customRunnable = new CustomRunnable();
        customRunnable.setCustomer("张三");
        Thread threadA = new Thread(customRunnable,"threadA");
        threadA.start();
        TimeUnit.SECONDS.sleep(1);
        threadA.join();
        System.out.println("滴滴师傅已到达,现在出发!");
    }

    static class CustomRunnable implements Runnable {

        private String customer;

        @SneakyThrows
        @Override
        public void run() {
            System.out.println(customer + "刚刚下单了,距离1km,预计3分钟到达!");
            // 使用当前线程对象作为锁对象
            synchronized (Thread.currentThread()) {
                System.out.println(Thread.currentThread().getName());
                TimeUnit.MINUTES.sleep(3);
            }
        }

        public void setCustomer(String customer) {
            this.customer = customer;
        }
    }
}

我们在CustomRunnablerun()方法中增加同步代码块,使用Thread.currentThread()作为锁对象,然后打印其名称得知为threadA,这时再来查看main线程的状态,发现其为State.BLOCKED,这也就说明join()方法的锁对象也为threadA。从而证明synchronized修饰非静态方法,锁对象是当前对象this,也就是调用者对象threadA

Thread.join()会使什么线程处于等待状态?_第5张图片

3.结论

调用Thread.join(),会使调用者所处的线程转换为State.WATING状态。

你可能感兴趣的:(java,java)