返回

多线程入门宝典:揭秘三个线程交替打印ABC 100次的奥秘

后端

征服并发编程:利用 Condition 变量巧妙解决经典面试题

在现代计算机世界中,并发编程已成为提升系统性能和响应速度的关键。然而,它也带来了诸多挑战,其中如何协调多个线程的执行尤为棘手。本文将深入探讨一个经典的面试题,并使用 Condition 变量揭示其巧妙的解决方案。

经典面试题:三个线程交替打印 ABC

假设有三个线程,要求它们按照 A、B、C 的顺序交替打印,每个字母打印 100 次。乍看之下,这似乎是一个简单的任务,但深入分析后你会发现,避免资源竞争和死锁是至关重要的。

Condition 变量:线程通信的利器

Java 并发库为我们提供了 Condition 变量,它可以实现线程间的通信和协调。Condition 变量包含两个关键方法:await() 和 signalAll()。当一个线程调用 await() 方法时,它会进入等待状态,直到被其他线程调用 signalAll() 方法唤醒。signalAll() 方法会唤醒所有正在等待该 Condition 变量的线程。

解决方案:Condition 变量的妙用

利用 Condition 变量,我们可以巧妙地解决三个线程交替打印 ABC 的问题。以下是具体步骤:

  1. 创建三个线程: 分别命名为 A、B 和 C。
  2. 创建 Condition 变量: 用于协调三个线程的执行。
  3. 线程 A 开始执行: 打印字母 A 100 次。
  4. 线程 A 执行完毕: 调用 signalAll() 方法唤醒其他两个线程。
  5. 线程 B 和 C 被唤醒: 分别打印字母 B 和 C 100 次。
  6. 线程 B 和 C 执行完毕: 再次调用 signalAll() 方法唤醒线程 A。
  7. 重复步骤 3-6: 直到三个线程都打印了 ABC 100 次。

代码示例:

以下是用 Java 实现的示例代码:

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

public class PrintABC {

    private static Lock lock = new ReentrantLock();
    private static Condition condition = lock.newCondition();

    private static int count = 0;

    public static void main(String[] args) {

        // 创建三个线程
        Thread threadA = new Thread(new Runnable() {
            @Override
            public void run() {
                while (count < 100) {
                    lock.lock();
                    try {
                        while (count % 3 != 0) {
                            condition.await();
                        }
                        System.out.print("A");
                        count++;
                        condition.signalAll();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            }
        });

        Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
                while (count < 100) {
                    lock.lock();
                    try {
                        while (count % 3 != 1) {
                            condition.await();
                        }
                        System.out.print("B");
                        count++;
                        condition.signalAll();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            }
        });

        Thread threadC = new Thread(new Runnable() {
            @Override
            public void run() {
                while (count < 100) {
                    lock.lock();
                    try {
                        while (count % 3 != 2) {
                            condition.await();
                        }
                        System.out.print("C");
                        count++;
                        condition.signalAll();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        lock.unlock();
                    }
                }
            }
        });

        // 启动三个线程
        threadA.start();
        threadB.start();
        threadC.start();
    }
}

总结:

利用 Condition 变量,我们成功地解决了三个线程交替打印 ABC 的问题,避免了资源竞争和死锁。理解线程通信机制对于解决并发编程中的挑战至关重要。

常见问题解答:

  1. Condition 变量和 wait()、notify() 方法有什么区别?
    Condition 变量是 Java 并发库中专门用于线程通信的类,而 wait() 和 notify() 方法是 Java 语言中的原生方法,需要使用同步块或同步方法。
  2. 在多线程环境中使用 Condition 变量时需要注意什么?
    使用 Condition 变量时,需要谨慎处理锁的获取和释放,以避免死锁或数据不一致。
  3. Condition 变量可以用于哪些类型的并发编程问题?
    Condition 变量可以用于各种并发编程问题,例如生产者-消费者问题、读写锁和屏障。
  4. 如何避免使用 Condition 变量时出现死锁?
    确保在使用 Condition 变量时始终正确获取和释放锁,并避免无限等待。
  5. Condition 变量和 Semaphore 变量有什么联系?
    Semaphore 变量是一种特殊的 Condition 变量,它用于控制线程访问资源的数量。