除了之前提到的几种方法(如join()、CountDownLatch、Semaphore、单线程池、synchronized和CompletableFuture)之外,确实还有一些其他方法能够保证线程按顺序执行。下面是一些常见的替代方案:
解锁Java多线程:如何控制线程T1、T2、T3的执行顺序(一)?
1. CyclicBarrier
解释: CyclicBarrier是Java并发包中的另一个同步工具,通常用于多个线程都到达某个共同的屏障点时一起继续执行。虽然它的主要用途是同步多个线程的执行,但也可以通过适当的设置来控制线程顺序。
使用场景: 当需要多个线程在同一时刻开始或继续执行,或者在特定时刻同步时,CyclicBarrier可以帮助实现这种控制。
代码示例:
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println("所有线程到达屏障点,开始执行");
});
Thread t1 = new Thread(() -> {
try {
System.out.println("线程T1开始");
barrier.await(); // 等待其他线程
System.out.println("线程T1继续");
} catch (Exception e) {
Thread.currentThread().interrupt();
}
});
Thread t2 = new Thread(() -> {
try {
System.out.println("线程T2开始");
barrier.await(); // 等待其他线程
System.out.println("线程T2继续");
} catch (Exception e) {
Thread.currentThread().interrupt();
}
});
Thread t3 = new Thread(() -> {
try {
System.out.println("线程T3开始");
barrier.await(); // 等待其他线程
System.out.println("线程T3继续");
} catch (Exception e) {
Thread.currentThread().interrupt();
}
});
t1.start();
t2.start();
t3.start();
2. Lock(显式锁)
解释: ReentrantLock 是一种显式的锁机制,比 synchronized 更加灵活。通过在多个线程间获取和释放锁,ReentrantLock 可以确保线程按顺序执行。它可以避免死锁,并允许指定公平锁(保证线程获取锁的顺序是按照请求顺序)。
使用场景: 当需要更多控制权和锁的灵活性时,ReentrantLock 是一个很好的选择。
代码示例:
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
Thread t1 = new Thread(() -> {
lock.lock();
try {
System.out.println("线程T1");
condition.signal(); // 唤醒T2
} finally {
lock.unlock();
}
});
Thread t2 = new Thread(() -> {
lock.lock();
try {
condition.await(); // 等待T1
System.out.println("线程T2");
condition.signal(); // 唤醒T3
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
});
Thread t3 = new Thread(() -> {
lock.lock();
try {
condition.await(); // 等待T2
System.out.println("线程T3");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
});
t1.start();
t2.start();
t3.start();
3. Exchanger
解释: Exchanger 是一个用于在两个线程之间交换数据的同步工具,通常用于两个线程在某个点交换信息时。通过这种机制,也可以实现线程间的顺序执行,尤其是在线程之间需要交换某些状态信息时。
使用场景: 适用于两个线程需要在某个同步点交换信息的场景。
代码示例:
Exchanger<String> exchanger = new Exchanger<>();
Thread t1 = new Thread(() -> {
try {
String result = "T1 finished";
exchanger.exchange(result); // 交换数据
System.out.println("线程T1");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Thread t2 = new Thread(() -> {
try {
exchanger.exchange(null); // 等待T1完成
System.out.println("线程T2");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Thread t3 = new Thread(() -> {
try {
exchanger.exchange(null); // 等待T2完成
System.out.println("线程T3");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
t1.start();
t2.start();
t3.start();
4. Phaser
解释: Phaser 是一种比 CountDownLatch 和 CyclicBarrier 更加灵活的同步工具,它支持动态注册和注销线程。它允许线程在不同的阶段同步,并且支持跨多个阶段的同步。
使用场景: 适用于多阶段的任务执行,尤其是线程执行的阶段数量不确定的情况下。
代码示例:
Phaser phaser = new Phaser(1); // 注册一个主线程
Thread t1 = new Thread(() -> {
System.out.println("线程T1");
phaser.arriveAndAwaitAdvance(); // 等待信号继续
});
Thread t2 = new Thread(() -> {
phaser.arriveAndAwaitAdvance(); // 等待T1
System.out.println("线程T2");
phaser.arriveAndAwaitAdvance(); // 准备继续下一阶段
});
Thread t3 = new Thread(() -> {
phaser.arriveAndAwaitAdvance(); // 等待T2
System.out.println("线程T3");
phaser.arriveAndAwaitAdvance(); // 结束
});
t1.start();
t2.start();
t3.start();
5. Thread.sleep()(不推荐)
解释: 虽然不推荐使用 Thread.sleep() 来控制线程顺序,但它可以简单地使线程休眠指定时间,间接地控制线程的执行顺序。这个方法的缺点是它不保证准确的顺序,并且可能导致性能问题。
使用场景: 在一些不那么严格的需求中,临时控制线程的执行顺序。
代码示例:
Thread t1 = new Thread(() -> {
try {
System.out.println("线程T1");
Thread.sleep(100); // 暂停一定时间
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Thread t2 = new Thread(() -> {
try {
Thread.sleep(100); // 等待T1稍微完成
System.out.println("线程T2");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Thread t3 = new Thread(() -> {
try {
Thread.sleep(200); // 等待T2完成
System.out.println("线程T3");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
t1.start();
t2.start();
t3.start();
总结
除了常见的 join()、CountDownLatch、Semaphore 等方法,其他同步工具如 CyclicBarrier、ReentrantLock、Exchanger 和 Phaser 也能帮助保证线程顺序执行。每种工具都有其适用的场景,选择合适的工具可以在确保线程执行顺序的同时,也提高程序的效率和可维护性。