2024-08-04
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://www.skjava.com/mianshi/baodian/detail/1663092096

回答

要想保证 T1T2T3 顺序执行,有多种方法。我们先新建一个 Tread:

class MyThread extends Thread {
    private String threadName;

    public MyThread(String name) {
        this.threadName = name;
    }

    @Override
    public void run() {
        System.out.println(threadName + " is running");
    }
}

一、使用 join()

join() 是 Java 提供的一种用于线程同步的机制,它能够让一个线程等待另一个线程完成后再继续执行。它有三个重载方法:

  1. join():无限等待,直到目标线程执行完毕。
  2. join(long millis):等待目标线程执行完毕,或者达到指定的毫秒时间。
  3. join(long millis, int nanos):等待目标线程执行完毕,或者达到指定的时间(以毫秒和纳秒为单位)。

使用 join() 来实现 T1T2T3 顺序执行,非常简单:

public class JoinTest {
    public static void main(String[] args) {
        MyThread T1 = new MyThread("T1");
        MyThread T2 = new MyThread("T2");
        MyThread T3 = new MyThread("T3");

        try {
            T1.start();
            T1.join(); // 等待 T1 执行完毕

            T2.start();
            T2.join(); // 等待 T2 执行完毕

            T3.start();
            T3.join(); // 等待 T3 执行完毕
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

// 执行结果
T1 is running
T2 is running
T3 is running

方法二:CountDownLatch

CountDownLatch 是一个同步辅助类,它允许一个或多个线程等待,直到在其他线程中执行的一组操作完成。

CountDownLatch 内部是通过一个计数器来实现的,当我们在构造一个CountDownLatch对象的时候需要传 入该计数器值,该值就表示了线程的数量。当一个线程完成自己的任务后,计数器的值就会减1。当计数器的值变为0时,就表示所有的线程均已经完成了任务,然后就可以恢复等待的线程继续执行了。

使用 CountDownLatch 来实现 T1T2T3 顺序执行,如下 :

class MyThread extends Thread {
    private String threadName;
    private CountDownLatch currentLatch;
    private CountDownLatch nextLatch;

    public MyThread(String name, CountDownLatch currentLatch, CountDownLatch nextLatch) {
        this.threadName = name;
        this.currentLatch = currentLatch;
        this.nextLatch = nextLatch;
    }

    @Override
    public void run() {
        try {
            currentLatch.await(); // 等待当前的 CountDownLatch 减到 0
            System.out.println(threadName + " is running.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            nextLatch.countDown(); // 减少下一个 CountDownLatch 的计数
        }
    }
}

public class CountDownLatchTest {
    public static void main(String[] args) {
        CountDownLatch latch1 = new CountDownLatch(1);
        CountDownLatch latch2 = new CountDownLatch(1);
        CountDownLatch latch3 = new CountDownLatch(1);

        MyThread T1 = new MyThread("T1", latch1, latch2);
        MyThread T2 = new MyThread("T2", latch2, latch3);
        MyThread T3 = new MyThread("T3", latch3, new CountDownLatch(0));

        T1.start();
        T2.start();
        T3.start();
        
        latch1.countDown(); // 启动 T1,计数 -1 变为 0
    }
}

在这里,我们创建了三个 CountDownLatch,分别用于控制 T1T2T3 的启动顺序。每个线程在启动前都等待当前的 CountDownLatch 变为 0,然后执行任务,并减少下一个 CountDownLatch 的计数。

当三个线程启动时,都会在那里等待,T1 等待latch1 变成 0T2 等待latch2 变成 0T3 等待latch3 变成 0。当在 main() 中执行 latch1.countDown() 时,T1 则启动执行,执行完成后执行 latch2.countDown() 驱动 T3 执行。

方案三 :Semaphore

Semaphore 是一个计数信号量,最常用于限制某个资源可被同时访问的线程数。

Semaphore是一个非负整数(>=1)。当一个线程想要访问某个共享资源时,它必须要先获取Semaphore,当Semaphore > 0时,获取该资源并使Semaphore – 1。如果Semaphore = 0,则表示全部的共享资源已经被其他线程全部占用,线程必须要等待其他线程释放资源。当线程释放资源时,Semaphore +1

使用 Semaphore 来实现 T1T2T3 顺序执行,如下 :

class MyThread extends Thread {
    private String threadName;
    private Semaphore currentSemaphore;
    private Semaphore nextSemaphore;

    public MyThread(String name, Semaphore currentSemaphore, Semaphore nextSemaphore) {
        this.threadName = name;
        this.currentSemaphore = currentSemaphore;
        this.nextSemaphore = nextSemaphore;
    }

    @Override
    public void run() {
        try {
            currentSemaphore.acquire(); // 获取当前的 Semaphore 许可
            System.out.println(threadName + " is running.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            nextSemaphore.release(); // 释放下一个 Semaphore 的许可
        }
    }
}

public class SemaphoreTest {
    public static void main(String[] args) {
        Semaphore semaphore1 = new Semaphore(1); // 初始信号量许可为 1
        Semaphore semaphore2 = new Semaphore(0); // 初始信号量许可为 0
        Semaphore semaphore3 = new Semaphore(0); // 初始信号量许可为 0

        MyThread T1 = new MyThread("T1", semaphore1, semaphore2);
        MyThread T2 = new MyThread("T2", semaphore2, semaphore3);
        MyThread T3 = new MyThread("T3", semaphore3, new Semaphore(0));

        T1.start();
        T2.start();
        T3.start();
    }
}

创建三个 Semaphore 实例,第一个的许可数为 1,其他两个为 0。每个线程在启动前都获取当前的 Semaphore 许可,执行任务后释放下一个 Semaphore 的许可。

最后,其实实现 T1T2T3 的方式有很多种,比如最笨的方式直接使用 wait() 就可以了,T1 wait(10)T2 wait(20)、T3 wait(30),一样可以得到。

阅读全文