现代并发模式

共 21 题
#

1. Go channel 的底层结构中 hchan 的环形缓冲区与等待队列(sendq/recvq)如何实现同步/异步收发,select 如何多路复用?

A select 对多个就绪 case 采用伪随机选择以保证公平,避免饥饿 ✓ 正确答案
B 有缓冲 channel 在缓冲区满时直接把数据丢弃而非阻塞
C 无缓冲 channel 使用环形缓冲区缓存数据,实现异步收发
D hchan 中的 sendq/recvq 只用于有缓冲 channel,无缓冲 channel 不需要等待队列
#

2. 线程池的"工作窃取"与"有界队列"两种调度模型分别解决什么问题?

A 有界队列模型通过无限扩容队列来吸收所有任务,无需拒绝策略
B 工作窃取模型让每个线程共享同一个队列,通过锁协调取任务
C 工作窃取从其他线程队列尾部偷取任务,利于任务局部性与负载均衡 ✓ 正确答案
D 有界队列总是优于工作窃取,因为实现更简单
#

3. 内存模型与 happens-before 中 volatile/CAS/加锁分别建立哪些可见性保证,为什么无同步共享变量会读到过期值

A 两个线程同时读写同一变量,只要不发生异常就一定能读到最新值
B 单线程内的程序顺序规则不构成 happens-before,因此重排完全自由
C volatile 写与后续对该 volatile 的读之间建立 happens-before,保证可见性 ✓ 正确答案
D CAS 不包含任何内存屏障,无法提供可见性保证
#

4. 死锁的预防与检测中锁排序、超时与 TryLock 如何预防,如何用线程转储检测循环等待

A 线程转储只能看到线程状态,无法判断锁的持有关系
B 死锁是并发必现的,无法通过代码设计预防
C 只要破坏四个必要条件中的任意一个,就能避免死锁 ✓ 正确答案
D TryLock 通过破坏互斥条件来避免死锁
#

5. 协程与线程的调度差异中为什么 goroutine 由 Go 运行时 M:N 调度而非 OS 线程,2KB 起始栈的动态增长与阻塞语义?

A goroutine 阻塞时通常不会阻塞整个 OS 线程,运行时会把 P 转给其他 goroutine ✓ 正确答案
B goroutine 起始栈固定为 2KB,运行中不会增长
C goroutine 由内核调度,切换开销与 OS 线程相同
D Go 始终使用一个 OS 线程执行所有 goroutine
#

6. 生产者-消费者模式在 Java(BlockingQueue)、Go(Channel)、Python(asyncio.Queue)中的不同实现?

A Python asyncio.Queue 是线程安全的,可被任意线程直接使用
B Go 的无缓冲 channel 提供同步握手,发送与接收必须同时就绪 ✓ 正确答案
C Java BlockingQueue 的 put 在队列空时阻塞消费者而非生产者
D 三种实现都基于多线程模型
#

7. 结构化并发(Structured Concurrency)中任务的创建与取消如何限定在词法作用域内,Kotlin coroutineScope 与 Java StructuredTaskScope 的实现?

A 子任务可以在父作用域结束后继续运行,实现发送即忘
B 子任务失败不会影响父作用域内的其他子任务
C 子任务的生命周期被限定在创建它的词法作用域内,父作用域结束会等待所有子任务 ✓ 正确答案
D coroutineScope 与 StructuredTaskScope 都基于 OS 线程实现
#

8. 读写锁、分段锁、无锁(CAS)三种并发策略在什么读写比下各有优势?

A CAS 在竞争激烈时仍能保持 O(1) 且无额外开销
B 分段锁把整张表锁住,任何写操作都会阻塞所有线程
C 读写锁在读多写少场景下能让多个读者共享,提高吞吐 ✓ 正确答案
D 写密集场景下读写锁一定优于分段锁
#

9. Actor 模型与线程模型的对比中消息传递与共享内存的差异,何时用 Actor 更合适?

A Actor 之间通过共享可变状态通信,与线程模型一致
B Actor 只适合单机场景,无法分布式部署
C 线程模型无法实现任何并发,必须依赖 Actor
D Actor 一次处理一个消息,状态被封装,天然避免数据竞争和死锁 ✓ 正确答案
#

10. Actor 模型与 CSP 模型的本质区别,分别适合什么场景?

A Actor 通过显式命名通道通信,CSP 通过目标地址通信
B CSP 中 channel 是通信媒介,可以作为一等公民传递和组合 ✓ 正确答案
C Go 的 channel 是 Actor 模型的典型实现
D 两者在通信方式上完全相同,只是名称不同
#

11. 线程 vs 事件循环中阻塞线程模型与单线程事件循环的伸缩性差异,Netty 的混合模型如何结合两者

A 线程每连接模型在连接数巨大时仍能保持很好的伸缩性
B Netty 的 Boss 线程负责所有连接的数据读写
C 单线程事件循环中执行阻塞操作会阻塞整个事件循环,影响所有连接 ✓ 正确答案
D 事件循环模型适合 CPU 密集而不是 IO 密集场景
#

12. 并发测试与竞态检测中为什么压测难以复现竞态,ThreadSanitizer 与 happens-before 静态分析的局限

A ThreadSanitizer 是运行时动态检测,能发现 TSan 实际执行到的数据竞争 ✓ 正确答案
B 压测通过一次运行就能稳定复现所有竞态
C 静态分析可以在编译期精确判定所有数据竞争,不存在漏报
D 竞态与线程时序无关,只要访问就会触发
#

13. Actor 的消息投递语义中邮箱通常不保证投递顺序以外的可靠性,at-most-once 投递如何影响分布式 Actor 设计(Akka/Orleans)?

A 从同一发送者到同一接收者的消息按顺序到达,但分布式下可能丢失或重复 ✓ 正确答案
B Actor 邮箱保证所有消息恰好送达一次
C 不同发送者之间的消息也保证严格全局顺序
D at-most-once 意味着消息一定不会丢失
#

14. 无锁编程的 CAS 原语中为什么 CAS 循环需要处理 ABA 问题(版本号/标记指针),与原子读改写(RMW)在弱内存模型下的配合?

A CAS 只比较值,所以 ABA 问题无法通过版本号解决
B ABA 问题指值在两次读取之间被改回原值,可用版本号或标记指针解决 ✓ 正确答案
C CAS 在弱内存模型下自动保证所有内存位置的有序性
D CAS 是一种阻塞式同步原语
#

15. Reactive Streams 的背压协议中 request(n) 如何控制发射速率,Reactor 的 subscribeOn/observeOn 如何切换线程与传播背压?

A observeOn 会断开背压传播,使下游可以任意接收
B Publisher 一次性把所有元素都推送给 Subscriber,与 request 无关
C subscribeOn 控制下游操作符所在线程
D Subscriber 通过 request(n) 限制自己的接收量,从而反向控制上游发射速率 ✓ 正确答案
#

16. 软件事务内存(STM)中 Clojure 的 STM 如何用 MVCC 快照+冲突重试保证原子性,与数据库事务在隔离与回滚上的异同?

A 数据库事务用 MVCC 重试,STM 用锁回滚
B STM 与数据库事务一样持久化到磁盘,具备持久性
C STM 使用悲观锁,冲突时阻塞等待
D STM 提交时检测到冲突会整体重试事务,而不是部分回滚 ✓ 正确答案
#

17. Actor 的容错与监管中 Erlang/Akka 的监督树(supervision tree)如何实现自愈?

A 子 Actor 崩溃时直接向调用者抛出异常,由调用者处理
B 监督树无法隔离错误,任一崩溃都会导致整个系统崩溃
C one-for-one 策略会重启所有子 Actor
D 监督者根据策略决定重启、停止或升级错误,实现自愈 ✓ 正确答案
#

18. Virtual Threads(Java 21)的调度中虚拟线程由 JVM 挂载到平台线程(carrier)执行,阻塞时如何卸载以释放载体线程?

A 虚拟线程直接映射到一个专属平台线程,阻塞时占住该线程
B 虚拟线程只能通过异步编程接口使用
C 虚拟线程与平台线程数量一一对应
D 虚拟线程阻塞时会卸载,释放载体线程去执行其他虚拟线程 ✓ 正确答案
#

19. 响应式流(Reactive Streams)的背压中 Publisher/Subscriber 的请求(request n)机制?

A Subscriber 通过 request(n) 限制 Publisher 的 onNext 调用次数,实现背压 ✓ 正确答案
B Publisher 可以无限推送元素,不受订阅者影响
C onError 之后还可以继续调用 onNext
D Subscription 只在订阅建立时用一次,之后无法再请求数据
#

20. 响应式宣言与背压中为什么背压是流式系统的核心,Push 模型的拥堵风险?

A Push 模型通过无界缓冲避免内存压力
B 背压是让生产者无限制推送,从而保证吞吐
C 响应式宣言包含即时响应、韧性、弹性、消息驱动四个特性 ✓ 正确答案
D 背压只在非分布式系统中才需要
#

21. 并发反模式中锁内做 IO、线程泄漏与共享可变状态为何难排查,对应的工程规避

A 共享可变状态只要不加锁就一定能顺序执行
B 线程泄漏问题在应用启动时就立即暴露,很容易发现
C 锁内做 IO 会长时间占住锁,降低吞吐并可能引发死锁 ✓ 正确答案
D 线程池中的线程数越多越好,能避免泄漏