JAVA線程-join

概念

join方法,一種特殊的wait,當前運行線程調用另外一個線程的join方法,當前線程進入阻塞狀態直到調用join方法的線程結束,再繼續執行。html

通常狀況下,都是主線程建立一個子線程,子線程調用join方法,主線程會進入阻塞狀態,直到子線程運行結束。git

簡單案例

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:github

主線程開始執行!
主線程執行結束!
線程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:jvm

主線程開始執行!
begin wait
線程1開始執行!
線程1執行了[1]次
線程1執行了[2]次
線程1執行了[3]次
線程1執行了[4]次
線程1執行了[5]次
thread wait
主線程執行結束!

thread1調用wait後,主線程阻塞,一直到子線程thread1運行結束退出之後,jvm會自動喚醒阻塞在thread1對象上的線程ide

那麼有沒有可能不使用thread1對象阻塞呢?post

下面就是不使用thread1對象阻塞主線程的案例.net

替代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?

相關文章
相關標籤/搜索