面试题:如何在 Java 中控制多个线程的执行顺序?

在 Java 中控制多个线程的执行顺序是一个常见的需求,尤其是在实现特定业务逻辑或算法时。有多种方式可以实现对线程执行顺序的控制,以下是几种常用的方法:

1. 使用 join() 方法

Thread.join() 方法允许一个线程等待另一个线程完成其执行之后再继续。这可以用来控制线程的执行顺序。

示例:

public class JoinExample {
    public static void main(String[] args) throws InterruptedException {
        Thread threadA = new Thread(() -> System.out.println("Thread A is running"));
        Thread threadB = new Thread(() -> {
            try {
                threadA.join(); // 等待 threadA 执行完毕
                System.out.println("Thread B is running");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        threadA.start();
        threadB.start();
    }
}

2. 使用 CountDownLatch

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

示例:

import java.util.concurrent.CountDownLatch;

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

        Thread threadA = new Thread(() -> {
            System.out.println("Thread A is running");
            latch.countDown(); // 减少计数
        });

        Thread threadB = new Thread(() -> {
            try {
                latch.await(); // 等待计数归零
                System.out.println("Thread B is running");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        threadB.start(); // 注意:先启动 threadB
        threadA.start(); // 再启动 threadA
    }
}

3. 使用 CyclicBarrier

CyclicBarrier 是一个可重复使用的屏障,它允许多个线程互相等待直到所有线程都到达某个公共屏障点。

示例:

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {
    public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(2, () -> System.out.println("Both threads have arrived at the barrier"));

        Thread threadA = new Thread(() -> {
            System.out.println("Thread A is running");
            try {
                barrier.await(); // 在屏障处等待
            } catch (InterruptedException | BrokenBarrierException e) {
                Thread.currentThread().interrupt();
            }
        });

        Thread threadB = new Thread(() -> {
            System.out.println("Thread B is running");
            try {
                barrier.await(); // 在屏障处等待
            } catch (InterruptedException | BrokenBarrierException e) {
                Thread.currentThread().interrupt();
            }
        });

        threadA.start();
        threadB.start();
    }
}

4. 使用 LockCondition

通过使用显式锁(如 ReentrantLock)和条件变量(Condition),可以更灵活地控制线程间的协作。

示例:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockConditionExample {
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();

    public void methodA() {
        lock.lock();
        try {
            System.out.println("Method A is running");
            condition.signalAll(); // 唤醒其他等待的线程
        } finally {
            lock.unlock();
        }
    }

    public void methodB() throws InterruptedException {
        lock.lock();
        try {
            condition.await(); // 等待被唤醒
            System.out.println("Method B is running after being notified");
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        LockConditionExample example = new LockConditionExample();

        Thread threadA = new Thread(example::methodA);
        Thread threadB = new Thread(() -> {
            try {
                example.methodB();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        threadB.start(); // 先启动 threadB,让它等待
        Thread.sleep(100); // 确保 threadB 先于 threadA 运行
        threadA.start(); // 启动 threadA 并触发通知
    }
}

总结

  • join() 最适合简单的线程顺序控制。
  • CountDownLatch 对于一次性事件的同步非常有用。
  • CyclicBarrier 提供了循环利用的屏障机制,适用于需要多次同步的场景。
  • LockCondition 提供了更加灵活的线程间通信手段,能够实现复杂的同步逻辑。

选择哪种方法取决于具体的应用场景以及你需要达到的具体效果。

THE END
喜欢就支持一下吧
点赞12 分享