如何确保三个线程按照特定顺序执行?
参考回答**
在 Java 中,可以通过使用同步机制(如 wait/notify
或 Lock/Condition
)或者并发工具类(如 CountDownLatch
或 Semaphore
)来确保三个线程按照特定顺序执行。例如,以下代码演示了使用 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();
}
}
优点: 可灵活控制线程的执行流程。
缺点: 对于复杂顺序控制,代码可能较冗长。
总结
- 简单场景:可以使用
join()
或CountDownLatch
,代码简洁,逻辑清晰。 - 复杂场景:
wait/notify
和Semaphore
提供更灵活的控制,但代码复杂度较高。 - 推荐方案:优先使用并发工具类(如
CountDownLatch
和Semaphore
),因为它们封装了线程安全机制,减少出错的可能性。