线程的执行顺序

线程的执行顺序是不确定的

调用Thread的start()方法启动线程时,线程的执行顺序时不确定的.也就是说,再同一个方法中,连续创建多个线程后,调用线程的start()方法的顺序并不能决定线程的执行顺序.

public class ThreadSort {

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            System.out.println("thread1");
        },"thread1");
        Thread thread2 = new Thread(() -> {
            System.out.println("thread2");
        },"thread2");
        Thread thread3 = new Thread(() -> {
            System.out.println("thread3");
        },"thread3");

        thread1.start();
        thread2.start();
        thread3.start();
    }
    
}
  • 第一次运行
thread2
thread1
thread3
  • 第二次运行
thread1
thread3
thread2
  • 第三次运行
thread3
thread2
thread1

每个人执行的结果可能不一样,但是可以确认的是,每次执行的时候,线程的执行顺序可能不同.线程启动顺序并不能决定线程的执行顺序.

使用join()确保线程的执行顺序

再实际业务场景中,有时候后启动的线程可能需要依赖先启动的线程执行完成才能正确的执行线程中的业务逻辑.此时,就需要确保线程的执行顺序.可以使用Thread的join()方法来确保线程的执行顺序

public class ThreadSort {

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            System.out.println("thread1");
        },"thread1");
        Thread thread2 = new Thread(() -> {
            System.out.println("thread2");
        },"thread2");
        Thread thread3 = new Thread(() -> {
            System.out.println("thread3");
        },"thread3");

        thread1.start();
        thread1.join();
        thread2.start();
        thread2.join();
        thread3.start();
        thread3.join();
    }
}
  • 第一次运行
thread1
thread2
thread3
  • 第二次运行
thread1
thread2
thread3
  • 第三次运行
thread1
thread2
thread3

可以看到,每次运行的结果都是相同的,并且是按照线程的启动顺序来执行的,所以,使用Thread的join()方法能够保证线程的先后执行顺序.

join()方法是如何确保线程的执行顺序

Thread.join()

public final void join() throws InterruptedException {
    join(0);
}

Thread.join(long)

join()方法里面调用了自己的有参的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) {
        while (isAlive()) {
            wait(0);
        }
    } else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

有参的join(long)方法使用了synchronized关键字修饰,说明这个方法同一时间只能被一个实例或者方法调用.由于调用时传递的参数为0,所以会进入以下逻辑

if (millis == 0) {
    // 以while循环的方式判断当前线程是否已经启动处于活跃状态
    // 如果已经处于活跃状态,调用wait()传递参数0
    while (isAlive()) {
        wait(0);
    }
}

Thread.isAlive()

/**
 * 本地方法
 * 判断线程是否已经启动处于活跃状态;如果已经处于活跃状态返回true
 *
 * @return
 */
public final native boolean isAlive();

Thread.wait(long)

/**
 * 本地方法
 * 通过JNI的方式调用JDK底层的方法使线程等待执行完成 
 *
 * @param      timeout
 *
 */
public final native void wait(long timeout) throws InterruptedException;

总结

调用线程的wait()方法时,会使主线程处于等待状态,等待子线程执行完成后再次向下执行.也就是说,再ThreadSort类的main()方法中,调用thread1的join()方法,会阻塞main()方法的执行,当thread1执行完成后,main()方法继续向下执行.启动thread2,调用thread2.join()方法…以此类推,确保线程按照线程的启动顺序来执行