JAVA线程-join

概念

join方法,一种特殊的wait,当前运行线程调用另一个线程的join方法,当前线程进入阻塞状态直到调用join方法的线程结束,再继续执行。

一般情况下,都是主线程创建一个子线程,子线程调用join方法,主线程会进入阻塞状态,直到子线程运行结束。

简单案例

public class JoinThreadDemo {

    public static void main(String[] args) {
        JoinRunnable runnable1 = new JoinRunnable();
        Thread thread1 = new Thread(runnable1, "线程1");
        System.out.println("主线程开始执行!");
        thread1.start();
//      try {
//          thread1.join();
//      } catch (InterruptedException e) {
//          e.printStackTrace();
//      }

        System.out.println("主线程执行结束!");
    }

    static final class JoinRunnable implements Runnable {
        @Override
        public void run() {
            String name = Thread.currentThread().getName();
            System.out.println(name + "开始执行!");

            for (int i = 1; i <= 5; i++) {
                System.out.println(name + "执行了[" + i + "]次");
            }
        }
    }
}

Output:

主线程开始执行!
主线程执行结束!
线程1开始执行!
线程1执行了[1]次
线程1执行了[2]次
线程1执行了[3]次
线程1执行了[4]次
线程1执行了[5]次

取消注释,调用join方法:

主线程开始执行!
线程1开始执行!
线程1执行了[1]次
线程1执行了[2]次
线程1执行了[3]次
线程1执行了[4]次
线程1执行了[5]次
主线程执行结束!

替代join案例

public class JoinThreadDemo {

    public static void main(String[] args) {
        JoinRunnable runnable1 = new JoinRunnable();
        Thread thread1 = new Thread(runnable1, "线程1");
        System.out.println("主线程开始执行!");
        thread1.start();
        try {
            synchronized (thread1) {
                while (thread1.isAlive()) {
                    System.out.println("begin wait");
                    //主线程持有thread1对象锁,阻塞,一直到thread1运行结束,jvm唤醒
                    thread1.wait(0);
                    System.out.println("thread wait");
                }
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("主线程执行结束!");

    }

    static final class JoinRunnable implements Runnable {

        @Override
        public void run() {
            String name = Thread.currentThread().getName();
            System.out.println(name + "开始执行!");

            for (int i = 1; i <= 5; i++) {
                System.out.println(name + "执行了[" + i + "]次");
            }
        }
    }
}

Output:

主线程开始执行!
begin wait
线程1开始执行!
线程1执行了[1]次
线程1执行了[2]次
线程1执行了[3]次
线程1执行了[4]次
线程1执行了[5]次
thread wait
主线程执行结束!

thread1调用wait后,主线程阻塞,一直到子线程thread1运行结束退出以后,jvm会自动唤醒阻塞在thread1对象上的线程

那么有没有可能不使用thread1对象阻塞呢?

下面就是不使用thread1对象阻塞主线程的案例

替代join案例2

public class JoinThreadDemo {

    public static void main(String[] args) {
        Object lock = new Object();
        Thread thread1 = new Thread(() -> {
            String name = Thread.currentThread().getName();
            System.out.println(name + "开始执行!");

            for (int i = 1; i <= 5; i++) {
                System.out.println(name + "执行了[" + i + "]次");
            }
        }, "线程1");
        System.out.println("主线程开始执行!");
        thread1.start();

        //thread2自旋唤醒阻塞在lock对象上的主线程
        Thread thread2 = new Thread(new Thread() {
            @Override
            public void run() {
                while (!thread1.isAlive() && !Thread.currentThread().isInterrupted()) {
                    synchronized (lock) {
                        System.out.println("enter");
                        lock.notifyAll();
                        System.out.println("exit");
                    }
                }
            }
        }, "线程2");
        thread2.start();

        try {
            synchronized (lock) {
                while (thread1.isAlive()) {
                    System.out.println("bb");
                    lock.wait();
                    //停止thread2自旋
                    thread2.interrupt();
                    System.out.println("tt");
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("主线程执行结束!");
    }
}

Output:

主线程开始执行!
bb
线程1开始执行!
线程1执行了[1]次
线程1执行了[2]次
线程1执行了[3]次
线程1执行了[4]次
线程1执行了[5]次
enter
exit
enter
exit
tt
主线程执行结束!

这里添加了一个线程thread2用于专门自旋唤醒主线程

用于替换案例一中的,thread1线程结束后,jvm唤醒主线程操作

join原理

阻塞主线程

Thread类中,join源码:

//Thread类中
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) {    //这个分支是无限期等待直到b线程结束
        while (isAlive()) {
            wait(0);
        }
    } else {    //这个分支是等待固定时间,如果b没结束,那么就不等待了。
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

join成员方法中有synchronized,锁定的对象为调用该方法的对象,即子线程thread1,即主线程持有了thread1对象锁

可以自己调试,发现进入到join源码的线程为主线程

唤醒主线程

子线程thread1执行完毕的时候,jvm会自动唤醒阻塞在thread1对象上的线程,在我们的例子中也就是主线程。至此,thread1线程对象被notifyall了,那么主线程也就能继续跑下去了

参考

Thread类中的join()方法原理

Java Thread的join() 之刨根问底

Java多线程初探——yield()方法与join()方法

sleep、yield、wait、join的区别(阿里)

who and when notify the thread.wait() when thread.join() is called?

猜你喜欢

转载自www.cnblogs.com/hongdada/p/11739311.html