面试官:来讲一下什么是线程通信

78 阅读8分钟

在网上混迹多年,我的ID叫“尘民1024”。“尘民”取自《灵笼·灯塔》,而“1024”是程序员心中的无限可能。祝愿大家都能从“尘民”走向“上民”。

20567f5754b88676c1065ebdbcd39fe5.jpg

线程通信,通信这个词我们通常会理解为发消息,其实,线程通信 ≠ 网络通信,不是“发消息”,而是多个线程之间协调工作、交换信息的过程,目的是解决线程间的协作问题(如避免资源竞争、实现任务配合等)

一、先搞懂:为什么线程需要通信?

在单线程程序中,代码是按顺序一步步执行的,不存在“协作”问题。但在多线程程序中,情况就完全不同了:

  • 线程的执行顺序是由 CPU 调度决定的,不可预测、不可控制
  • 多个线程可能会同时访问和修改同一份共享数据

如果没有通信机制来保证有序协作,通常会引发以下典型问题:

  1. 数据不一致(资源竞争)
    例如两个线程同时对共享变量 count 执行 count++ 操作,由于自增不是原子操作,就可能导致最终结果小于预期。

    public class InventoryRaceDemo {
        private static int stock = 100000; // 商品库存
    
        public static void main(String[] args) throws InterruptedException {
            Runnable purchaseTask = () -> {
                for (int i = 0; i < 50000; i++) {
                    stock--; // 非原子操作,库存可能出错
                }
            };
    
            Thread user1 = new Thread(purchaseTask);
            Thread user2 = new Thread(purchaseTask);
    
            user1.start();
            user2.start();
            user1.join();
            user2.join();
    
            System.out.println("剩余库存: " + stock);
            // 预期库存 0,但可能出现负数或库存不准确,测试剩余库存: 38826
        }
    }
    

    说明:stock-- 不是原子操作,导致“丢失更新”,可能卖出超过实际库存。。

  2. 线程协作失控
    假设一个线程负责生产数据,另一个线程负责消费数据,如果没有通信机制,消费者可能在生产者还没准备好数据时就开始消费,导致“空取”;或者生产者疯狂生产,消费者没来得及处理,导致“堆积”。

    public class OrderQueueDemo {
        private static final Queue<String> orderQueue = new LinkedList<>();
        private static final int CAPACITY = 5;
    
        public static void main(String[] args) {
            Thread orderProducer = new Thread(() -> {
                int orderId = 1;
                while (true) {
                    if (orderQueue.size() < CAPACITY) {
                        String order = "订单#" + orderId++;
                        orderQueue.add(order);
                        System.out.println("生成订单: " + order);
                    }
                }
            });
    
            Thread orderConsumer = new Thread(() -> {
                while (true) {
                    if (!orderQueue.isEmpty()) {
                        String order = orderQueue.poll();
                        System.out.println("处理订单: " + order);
                    }
                }
            });
    
            orderProducer.start();
            orderConsumer.start();
        }
    }
    
    
    生成订单: 订单#1
    生成订单: 订单#2
    生成订单: 订单#3
    生成订单: 订单#4
    生成订单: 订单#5
    
    

    分析:

    1. 队列已经满(达到 CAPACITY = 5)

    2. 生产者停止生成新订单

    3. 消费者尚未消费

    4. 出现堆积现象:生产者生成了订单,但消费者还没来得及消费,队列堆满。

    说明:没有通信机制,生产者和消费者无法协调,出现堆积问题。

  3. 死锁/饥饿问题
    多个线程互相等待对方释放资源,如果没有合理的通信手段协调,就可能造成所有线程都无法继续执行(死锁);或者部分线程长期得不到执行机会(饥饿)。

    public class EcommerceDeadlockDemo {
        private static final Object orderLock = new Object();
        private static final Object inventoryLock = new Object();
    
        public static void main(String[] args) {
            Thread t1 = new Thread(() -> {
                synchronized (orderLock) {
                    System.out.println("线程1 获取订单锁");
                    try { Thread.sleep(100); } catch (InterruptedException e) {}
                    synchronized (inventoryLock) {
                        System.out.println("线程1 获取库存锁");
                    }
                }
            });
    
            Thread t2 = new Thread(() -> {
                synchronized (inventoryLock) {
                    System.out.println("线程2 获取库存锁");
                    try { Thread.sleep(100); } catch (InterruptedException e) {}
                    synchronized (orderLock) {
                        System.out.println("线程2 获取订单锁");
                    }
                }
            });
    
            t1.start();
            t2.start();
        }
    }
    
    

    说明:线程1等待库存锁,线程2等待订单锁,导致死锁。

  4. 状态不可见(内存可见性问题)
    由于 CPU 缓存和编译优化的存在,一个线程修改了共享变量,另一个线程不一定能立即看到最新值,从而导致逻辑错误。

    
    public class SeckillVisibilityDemo {
       private static boolean open = true; // 秒杀是否开启
    
       public static void main(String[] args) throws InterruptedException {
           Thread worker = new Thread(() -> {
               while (open) {
                   // 模拟秒杀任务
               }
               System.out.println("秒杀任务结束");
           });
           worker.start();
    
           Thread.sleep(1000);
           open = false; // 主线程关闭秒杀
           System.out.println("主线程修改 open=false");
       }
    }
    

    说明:worker 线程可能看不到 open=false,导致秒杀任务继续执行。


👉 因此,多线程编程离不开**通信**。通信机制的核心目标是:
  • 保证数据一致性(不会因为竞争导致错误结果)
  • 协调执行顺序(让线程按约定的逻辑配合工作)
  • 提升系统健壮性(避免死锁、饥饿、乱序等问题)

二、Java 中的线程通信方式

分类典型实现通信方向适用场景
基础锁通信synchronized + wait()/notify()任意线程间双向通知简单同步/等待-唤醒场景
条件锁通信Lock + Condition任意线程间精确通知多条件判断 / 精细控制
阻塞队列通信BlockingQueue生产者 → 消费者生产者-消费者模型
信号量通信Semaphore任意线程获取/释放许可限流 / 资源池管理
同步屏障通信(一次性)CountDownLatch子线程 → 主线程启动等待 / 结果汇总
同步屏障通信(可复用)CyclicBarrier多线程 ↔ 多线程分阶段任务协同
轻量级状态共享volatile 变量单向状态通知开关控制 / 标志位
任务控制Thread.interrupt()请求线程 → 执行线程任务取消 / 超时处理
线程等待join()主线程 ← 子线程等待子线程执行结束

1. 基础锁通信

典型实现

  • wait() / notify() / notifyAll()

适用场景

  • 简单的线程间同步、通知。

示例代码

public class OrderProcessingDemo {
    private static final Queue<String> orderQueue = new LinkedList<>();
    private static final int MAX_CAPACITY = 2;

    public static void main(String[] args) {
        Object lock = new Object();

        Thread orderProducer = new Thread(() -> {
            int orderId = 1;
            while (true) {
                synchronized (lock) {
                    while (orderQueue.size() >= MAX_CAPACITY) {
                        try {
                            lock.wait(); // 队列满,等待订单处理
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    String order = "订单#" + orderId++;
                    orderQueue.add(order);
                    System.out.println("生成订单: " + order);
                    lock.notifyAll(); // 通知处理线程
                }
            }
        });

        Thread orderConsumer = new Thread(() -> {
            while (true) {
                synchronized (lock) {
                    while (orderQueue.isEmpty()) {
                        try {
                            lock.wait(); // 队列空,等待新订单
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    String order = orderQueue.poll();
                    System.out.println("处理订单: " + order);
                    lock.notifyAll(); // 通知生产线程
                }
            }
        });

        orderProducer.start();
        orderConsumer.start();
    }
}

2. 条件锁通信

典型实现

  • Lock + Condition

适用场景

  • 多条件精确通知,例如生产者-消费者模型中不同条件下的等待。

示例代码

public class InventoryOrderDemo {
    private static final Queue<String> orderQueue = new LinkedList<>();
    private static final int MAX_CAPACITY = 1;

    private static final Lock lock = new ReentrantLock();
    private static final Condition notFull = lock.newCondition();
    private static final Condition notEmpty = lock.newCondition();

    public static void main(String[] args) {
        Thread producer = new Thread(() -> {
            int orderId = 1;
            while (true) {
                lock.lock();
                try {
                    while (orderQueue.size() >= MAX_CAPACITY) {
                        notFull.await(); // 队列满,等待处理
                    }
                    String order = "订单#" + orderId++;
                    orderQueue.add(order);
                    System.out.println("新订单生成: " + order);
                    notEmpty.signal(); // 唤醒处理线程
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        });

        Thread consumer = new Thread(() -> {
            while (true) {
                lock.lock();
                try {
                    while (orderQueue.isEmpty()) {
                        notEmpty.await(); // 队列空,等待新订单
                    }
                    String order = orderQueue.poll();
                    System.out.println("处理订单: " + order);
                    notFull.signal(); // 唤醒生产线程
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        });

        producer.start();
        consumer.start();
    }
}

3. 阻塞队列通信

典型实现

  • BlockingQueue

适用场景

  • 生产者-消费者模型。

示例代码

public class PaymentQueueDemo {
    private final BlockingQueue<String> paymentQueue = new ArrayBlockingQueue<>(5);

    public void producePayment() throws InterruptedException {
        for (int i = 1; i <= 5; i++) {
            String payment = "支付订单#" + i;
            System.out.println("生成支付请求:" + payment);
            paymentQueue.put(payment);
        }
    }

    public void processPayment() throws InterruptedException {
        while (true) {
            String payment = paymentQueue.take();
            System.out.println("处理支付:" + payment);
        }
    }

    public static void main(String[] args) {
        PaymentQueueDemo demo = new PaymentQueueDemo();
        new Thread(() -> {
            try {
                demo.processPayment();
            } catch (Exception e) {
            }
        }).start();
        new Thread(() -> {
            try {
                demo.producePayment();
            } catch (Exception e) {
            }
        }).start();
    }
}

4. 信号量通信

典型实现

  • Semaphore

适用场景

  • 控制资源访问许可数量,如限流、连接池。

示例代码

public class SeckillDemo {
    private final Semaphore semaphore = new Semaphore(3);

    public void attemptPurchase(int userId) throws InterruptedException {
        semaphore.acquire();
        try {
            System.out.println("用户 " + userId + " 抢购成功,处理中...");
            Thread.sleep(1000); // 模拟处理时间
        } finally {
            System.out.println("用户 " + userId + " 完成抢购,释放名额");
            semaphore.release();
        }
    }

    public static void main(String[] args) {
        SeckillDemo demo = new SeckillDemo();
        for (int i = 1; i <= 6; i++) {
            int userId = i;
            new Thread(() -> {
                try {
                    demo.attemptPurchase(userId);
                } catch (Exception e) {
                }
            }).start();
        }
    }
}

5. 同步屏障通信

典型实现

  • CountDownLatch
  • CyclicBarrier

适用场景

  • CountDownLatch:主线程等待子任务完成。
  • CyclicBarrier:多个线程分阶段协作。

示例代码

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

        for (int i = 1; i <= 3; i++) {
            int taskId = i;
            new Thread(() -> {
                System.out.println("订单拆分任务 " + taskId + " 完成");
                latch.countDown();
            }).start();
        }

        latch.await();
        System.out.println("所有订单拆分完成,开始统一发货");
    }
}

public class DeliveryBarrierDemo {
    public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.println("所有配送任务到达,开始装车发货"));

        for (int i = 1; i <= 3; i++) {
            int deliveryId = i;
            new Thread(() -> {
                try {
                    System.out.println("配送任务 " + deliveryId + " 到达仓库");
                    barrier.await();
                    System.out.println("配送任务 " + deliveryId + " 开始配送");
                } catch (Exception e) {}
            }).start();
        }
    }
}

6. 状态变量通信 典型实现

  • 典型实现volatile
  • 适用场景:单向状态通知,如开关标志。
  • 示例代码
public class SeckillSwitchDemo {
    private volatile boolean open = true;

    public void start() {
        new Thread(() -> {
            while (open) {
                // 模拟秒杀任务
            }
            System.out.println("秒杀结束");
        }).start();
    }

    public void stop() {
        open = false;
    }

    public static void main(String[] args) throws InterruptedException {
        SeckillSwitchDemo demo = new SeckillSwitchDemo();
        demo.start();
        Thread.sleep(1000);
        demo.stop();
    }
}

7. 任务中断通信

典型实现: Thread.interrupt()

适用场景: 任务取消、超时控制。

示例代码:

public class OrderTimeoutDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread orderWorker = new Thread(() -> {
            try {
                while (!Thread.currentThread().isInterrupted()) {
                    System.out.println("订单处理中...");
                    Thread.sleep(500);
                }
            } catch (InterruptedException e) {
                System.out.println("订单处理超时,中断取消");
            }
        });

        orderWorker.start();
        Thread.sleep(2000);
        orderWorker.interrupt();
    }
}

三、总结:怎么理解 Java 的线程通信?

  1. 底层依赖:共享内存模型 (JMM)

    • Java 内存模型 (JMM) 保证了 volatile、锁等关键字的可见性、有序性、原子性
  2. 通信 ≠ 消息传递,而是“可见性 + 协作”

    • 通信手段本质上是保证:
      • 线程 A 的写操作 对线程 B 可见
      • 线程 A 能 通知 线程 B:某个条件成立了。
  3. 选型思路:根据业务场景选择合适机制

    • 简单同步 → synchronized + wait/notify
    • 复杂条件 → Lock + Condition
    • 任务协作 → CountDownLatch / CyclicBarrier
    • 生产消费 → BlockingQueue
    • 状态通知 → volatile / interrupt

一句话记忆
Java 线程通信,本质是 通过共享内存可见性 + 各种同步工具类
实现线程间的 通知、等待、协作