如何确保三个线程按照特定顺序执行?

参考回答**

在 Java 中,可以通过使用同步机制(如 wait/notifyLock/Condition)或者并发工具类(如 CountDownLatchSemaphore)来确保三个线程按照特定顺序执行。例如,以下代码演示了使用 join() 方法让线程按照指定顺序执行:

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

        thread1.start();
        thread1.join(); // 等待线程1完成

        thread2.start();
        thread2.join(); // 等待线程2完成

        thread3.start();
    }
}

通过 join() 方法,线程2会等待线程1执行完成后再启动,线程3会等待线程2执行完成后再启动,从而实现顺序执行。


详细讲解与拓展

1. 使用 join()

join() 方法会让当前线程等待另一个线程执行完成。其核心原理是主线程调用线程对象的 join() 方法时,会进入阻塞状态,直到目标线程结束。

示例:

Thread t1 = new Thread(() -> System.out.println("线程1执行"));
Thread t2 = new Thread(() -> System.out.println("线程2执行"));
Thread t3 = new Thread(() -> System.out.println("线程3执行"));

t1.start();
t1.join(); // 等待 t1 完成

t2.start();
t2.join(); // 等待 t2 完成

t3.start();
t3.join(); // 等待 t3 完成

优点: 简单直接。
缺点: 线程之间的耦合度较高,不能灵活实现更复杂的顺序控制。


2. 使用 wait/notify

可以通过 wait/notify 机制,使用共享的锁和标志变量来控制线程执行顺序。

示例:

public class WaitNotifyExample {
    private static final Object lock = new Object();
    private static int flag = 1;

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            synchronized (lock) {
                while (flag != 1) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("线程1执行");
                flag = 2;
                lock.notifyAll();
            }
        });

        Thread t2 = new Thread(() -> {
            synchronized (lock) {
                while (flag != 2) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("线程2执行");
                flag = 3;
                lock.notifyAll();
            }
        });

        Thread t3 = new Thread(() -> {
            synchronized (lock) {
                while (flag != 3) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("线程3执行");
                lock.notifyAll();
            }
        });

        t1.start();
        t2.start();
        t3.start();
    }

优点: 可以精确控制线程的执行顺序。
缺点: 编程较复杂,代码容易出错。


3. 使用 CountDownLatch

CountDownLatch 是一个并发工具类,可以通过设置初始计数值实现线程间的协调。

示例:

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch1 = new CountDownLatch(1);
        CountDownLatch latch2 = new CountDownLatch(1);

        Thread t1 = new Thread(() -> {
            System.out.println("线程1执行");
            latch1.countDown(); // 减少 latch1 的计数
        });

        Thread t2 = new Thread(() -> {
            try {
                latch1.await(); // 等待 latch1 减为 0
                System.out.println("线程2执行");
                latch2.countDown(); // 减少 latch2 的计数
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread t3 = new Thread(() -> {
            try {
                latch2.await(); // 等待 latch2 减为 0
                System.out.println("线程3执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        t1.start();
        t2.start();
        t3.start();
    }
}

优点: 使用方便,代码更清晰。
缺点: CountDownLatch 是一次性的,不能重复使用。


4. 使用 Semaphore

Semaphore 是另一种常用的并发工具类,可以通过限制线程的许可证数量来控制线程的执行顺序。

示例:

import java.util.concurrent.Semaphore;

public class SemaphoreExample {
    public static void main(String[] args) {
        Semaphore semaphore1 = new Semaphore(1);
        Semaphore semaphore2 = new Semaphore(0);
        Semaphore semaphore3 = new Semaphore(0);

        Thread t1 = new Thread(() -> {
            try {
                semaphore1.acquire();
                System.out.println("线程1执行");
                semaphore2.release(); // 释放 semaphore2 的许可证
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread t2 = new Thread(() -> {
            try {
                semaphore2.acquire();
                System.out.println("线程2执行");
                semaphore3.release(); // 释放 semaphore3 的许可证
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        Thread t3 = new Thread(() -> {
            try {
                semaphore3.acquire();
                System.out.println("线程3执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        t1.start();
        t2.start();
        t3.start();
    }
}

优点: 可灵活控制线程的执行流程。
缺点: 对于复杂顺序控制,代码可能较冗长。


总结

  1. 简单场景:可以使用 join()CountDownLatch,代码简洁,逻辑清晰。
  2. 复杂场景wait/notifySemaphore 提供更灵活的控制,但代码复杂度较高。
  3. 推荐方案:优先使用并发工具类(如 CountDownLatchSemaphore),因为它们封装了线程安全机制,减少出错的可能性。

发表评论

后才能评论