如何确保线程的执行顺序?

2023-05-24 14:47:13 浏览数 (2)

前言

线程的执行顺序是不确定的:在同一个方法中,连续创建多个线程,调用线程的start()方法的顺序并不能决定线程的执行顺序。

案例

代码语言:javascript复制
package com.xl.bcst.juc.thread.sequence;
/**
  公众号:编程识堂
**/
public class ExecuteSeqDemo {

     public static void main(String[] args) {
      Thread thread1 =    new Thread(()->{
            System.out.println("线程1执行了。。。。。");
        },"线程1");
        Thread thread2 =     new Thread(()->{
            System.out.println("线程2执行了。。。。。");
        },"线程2");
        Thread thread3 =    new Thread(()->{
            System.out.println("线程3执行了。。。。。");
        },"线程3");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

在ExecuteSeqDemo类中分别创建了三个不同的线程,线程1、线程2和线程3,并调用start方法启动了三个不同的线程, 那么,问题来了,线程的执行顺序是否按照线程1、线程2和线程3的顺序执行呢?运行ExecuteSeqDemo的main方法,结果如下所示。

代码语言:javascript复制
//第一次执行
线程1执行了。。。。。
线程3执行了。。。。。
线程2执行了。。。。。
//第二次执行
线程1执行了。。。。。
线程2执行了。。。。。
线程3执行了。。。。。

注意:每个人运行的情况可能都不一样。

可以看到,每次运行程序时,线程的执行顺序可能不同。线程的启动顺序并不能决定线程的执行顺序。

如何确保线程的执行顺序

确保线程执行顺序的简单示例

在实际业务场景中,有时,后启动的线程可能需要依赖先启动的线程执行完成才能正确的执行线程中的业务逻辑。此时,就需要确保线程的执行顺序。

那么如何确保线程的执行顺序呢?可以使用Thread类中的join()方法来确保线程的执行顺序。例如,下面的测试代码。

代码语言:javascript复制
package com.xl.bcst.juc.thread.sequence;
/**
  公众号:编程识堂
**/
public class ExecuteSeqDemo {

    public static void main(String[] args) throws InterruptedException {
      Thread thread1 =    new Thread(()->{
            System.out.println("线程1执行了。。。。。");
        },"线程1");
        Thread thread2 =     new Thread(()->{
            System.out.println("线程2执行了。。。。。");
        },"线程2");
        Thread thread3 =    new Thread(()->{
            System.out.println("线程3执行了。。。。。");
        },"线程3");

        thread1.start();
        thread1.join();
        thread2.start();
        thread2.join();
        thread3.start();
    }
}

运行结果

代码语言:javascript复制
线程1执行了。。。。。
线程2执行了。。。。。
线程3执行了。。。。。

运行结果后,我们发现每次运行的结果都是相同的,所以,使用Thread的join()方法能够保证线程的先后执行顺序。

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

首先我们看下join源码

代码语言:javascript复制
/**
无参构造方法会让当前线程处于等待状态,直到另外一个线程执行完毕
**/
public final void join() throws InterruptedException {
    join(0);
}
/**
 有参构造方法 带参数的方法会先计算出当前时间与调用 join() 
 方法时的时间差,然后根据指定的时间计算出需要等待的时间。
 如果等待时间小于等于 0,则直接跳出循环;否则,调用 wait() 
 方法等待一段时间,然后重新计算时间差。如果等待时间已经超过指定时间,
 则跳出循环
**/
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() 方法是 Thread 类中的一个方法,用来等待该线程执行完毕。具体来说,当一个线程调用另一个线程的 join() 方法时,该线程会处于等待状态,直到另一个线程执行完毕。上面的例子就是mian线程调用了thread1线程的join方法,所以main线程会处于等待,直到thread1线程执行完毕,会发送一个通知告知thread1执行完毕,main线程被唤醒,继续往下执行。

join() 方法是一个同步方法,它会获取当前线程的锁,因此其他线程无法同时调用该方法。同时,如果另一个线程在调用 join() 方法时被中断,那么该方法会抛出 InterruptedException 异常。

0 人点赞