面试官听了都点头!5 分钟彻底掌握 Java Condition 源码原理

18 阅读3分钟



引言:一次Java社招面试的“灵魂拷问”

最近,我去面试了一家知名互联网大厂的高级Java开发岗。结果在面试时,面试官问了一个让我汗流浃背的问题:

“你能详细讲解一下 Java 并发中的 Condition 是如何实现等待通知机制的吗?”

我当时心里咯噔一下,这个问题不简单,毕竟平时大家用 synchronized + wait/notify 解决生产者-消费者问题挺顺手,Condition 这块用得少,没仔细研究过源码。

于是,我调整状态,整理思路,决定用直白的方式讲解 Condition 的工作原理,并分析它的底层实现。今天,我把我的思考整理出来,希望对你有所帮助!

为什么要用 Condition?ReentrantLock 和 synchronized 有什么区别?

在 Java 并发编程中,我们通常有两种方式来实现线程间的同步:

  • 方式一:使用 synchronized + wait/notify
  • 方式二:使用 ReentrantLock + Condition

很多人都会问: “既然 wait/notify 可以实现等待通知机制,为什么还要 Condition 呢?”

这里,我们先来看 synchronized + wait/notify 存在哪些问题:

  • wait/notify 只能和 synchronized 搭配使用,而 synchronized 是隐式锁,不能灵活控制锁的获取和释放。
  • notify() 不能指定唤醒哪个线程,所有等待线程都在同一个队列里,可能导致“误唤醒”问题。
  • wait() 需要在 synchronized 代码块中调用,否则会抛 IllegalMonitorStateException。
  • synchronized 不能支持多个等待条件,notifyAll() 可能会导致不必要的唤醒,提高了系统开销。

而 ReentrantLock + Condition 能够完美地解决这些问题:

  • 更精细的控制锁的获取与释放 —— ReentrantLock 支持可重入、可中断、公平锁等特性,比 synchronized 更灵活。
  • 支持多个 Condition —— 一个 Lock 可以创建多个 Condition,每个 Condition 维护自己的等待队列,避免无效唤醒,提高性能。
  • 可以精准唤醒指定线程 —— condition.signal() 只会唤醒特定 Condition 队列中的线程,而不是像 notifyAll() 一样一股脑全唤醒。

所以,如果你的场景需要更精细的线程等待通知控制,那 ReentrantLock + Condition 绝对是你的最佳选择!

Condition 的基本用法:生产者-消费者模型

为了直观地展示 Condition 的用法,我们来看一个经典的生产者-消费者模型

运行结果(示例):

这个例子清楚地展示了 Condition 的用法:

  • notFull.await() 让生产者在队列满时进入等待状态,直到消费者消费后唤醒它。
  • notEmpty.await() 让消费者在队列空时进入等待状态,直到生产者生产后唤醒它。
  • notFull.signal() 唤醒等待的生产者,notEmpty.signal() 唤醒等待的消费者。

Condition 的精准唤醒能力,比 notifyAll() 更高效!

Condition 源码解析

Condition 是 Lock 绑定的条件变量,其实它的核心是AQS(AbstractQueuedSynchronizer)

1. await() 源码解析

当线程执行 await(),它会:

  • 释放当前持有的 Lock,让其他线程可以获取锁。
  • 进入 Condition 的等待队列,挂起自己(阻塞)。
  • 直到 signal() 被调用,才会重新尝试获取 Lock,然后继续执行。

2. signal() 源码解析

当某个线程调用 signal(),它会:

  • 找到 Condition 等待队列中的第一个节点。
  • 把这个节点移动到 AQS 的同步队列中,让它有资格去争夺锁。
  • 当这个线程拿到锁后,就可以继续执行。

总结

  • synchronized + wait/notify 是 Java 早期的等待通知机制,但不够灵活。
  • ReentrantLock + Condition 提供更细粒度的等待控制,可精准唤醒指定线程,提升性能。
  • Condition 的底层是基于 AQS 实现的,核心操作是 await() 和 signal(),配合 LockSupport 进行线程挂起和唤醒。

所以,如果你想掌握 Java 并发编程,一定要深入理解 Condition!

END

你在面试中遇到过类似的问题吗?欢迎留言交流!

我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号“软件求生”,获取更多技术干货!