并发与设计类手撕

共 19 题
#

1. 手写生产者-消费者中 wait/notify 与 BlockingQueue 两种实现的虚假唤醒防御?

A notify 与 notifyAll 效果完全一致
B wait 放在 if 中即可
C BlockingQueue 需要手写 wait/notify
D wait 必须放在 while 循环中,以防御虚假唤醒与条件变化 ✓ 正确答案
#

2. 三个线程交替打印 ABC 的多种实现(synchronized/Lock+Condition/Semaphore)?

A synchronized 用 notifyAll 时无法靠状态判断
B Semaphore 用三个信号量初始 1,0,0,各自 acquire 后 release 下一个,形成轮转 ✓ 正确答案
C Lock+Condition 用 notifyAll 唤醒所有
D 三种实现都需要自旋
#

3. 手写令牌桶限流器(Token Bucket)中字段设计(capacity/rate/tokens/lastRefillTime)、tryAcquire 的原子性保证(CAS vs synchronized)、分布式场景下 Redis + Lua 的实现

A 分布式下用普通 Redis 操作即可保证原子性
B 令牌桶无需记录上次补充时间
C 用 lastRefillTime 惰性补充令牌,tryAcquire 需保证检查+更新的原子性 ✓ 正确答案
D tryAcquire 无需原子性
#

4. 手写 LRU Cache(LeetCode 146)中 HashMap + 双向链表的 O(1) get/put、边界处理(capacity=0、重复 key 更新)、线程安全版本的锁粒度选择(全局锁 vs 分段锁 vs ConcurrentHashMap + 链表锁)

A 双向链表只支持 O(n) 删除
B HashMap 定位 + 双向链表 O(1) 删除任意节点,capacity=0 时 put 无效 ✓ 正确答案
C 重复 key 更新会新增节点
D get 未命中时也要移动链表
#

5. 手写限流的滑动窗口中如何用时间戳队列实现固定窗口与滑动窗口,边界请求如何处理?

A 滑动窗口内存 O(1)
B 固定窗口比滑动窗口更精确
C 滑动窗口用时间戳队列移除过期请求,避免固定窗口的边界突刺 ✓ 正确答案
D 滑动窗口无法处理边界请求
#

6. 手写时间轮/延迟任务调度器中环形数组加轮询指针如何调度延时任务,与 DelayQueue 堆实现的复杂度对比

A 时间轮用环形数组+指针,添加任务 O(1);DelayQueue 用堆,添加 O(log n) ✓ 正确答案
B 时间轮添加任务 O(log n)
C DelayQueue 添加任务 O(1)
D 时间轮无法处理大跨度延迟
#

7. 手写 Semaphore(计数信号量)中用 lock 加 condition 实现 acquire/release,为什么释放时要通知所有等待者?

A acquire 用 while(permits<=0) await,release 用 signalAll 通知所有等待者 ✓ 正确答案
B release 只需 signal 一个等待者
C acquire 无需循环检查
D 释放后无需通知等待者
#

8. 手写线程安全的单例(双检锁 volatile 的必要性)?

A volatile 防止实例创建时指令重排,避免暴露未初始化对象 ✓ 正确答案
B 双检锁无需 volatile
C 第一层检查必须加锁
D volatile 只保证可见性,不涉及有序性
#

9. 设计一个线程安全的计数器,synchronized、AtomicLong、LongAdder 的性能层级?

A AtomicLong 是阻塞式
B 高并发写场景性能层级为 LongAdder > AtomicLong > synchronized ✓ 正确答案
C LongAdder 单点竞争
D synchronized 在并发下性能最好
#

10. 手写 ReadWriteLock 中读读并发、读写互斥、写写互斥的状态位设计(高 16 位读锁/低 16 位写锁)、写锁降级与读锁升级的禁止原因、公平与非公平策略

A 读读互斥
B 读锁升级是被允许的
C 写锁降级被禁止
D 高 16 位读锁、低 16 位写锁;写锁可降级为读锁,读锁升级被禁止(防死锁) ✓ 正确答案
#

11. 手写 CountDownLatch 与 CyclicBarrier 中 AQS 的 state 计数与 await/signal 机制、CyclicBarrier 的代(generation)概念与 BrokenBarrierException、两者在并发测试框架中的应用

A CountDownLatch 一次性倒计数,CyclicBarrier 可重用且用 generation 表示每轮 ✓ 正确答案
B 两者都可无限重用
C CyclicBarrier 无中断异常
D CountDownLatch 的 state 递增
#

12. 多线程交替打印中用 volatile/自旋、锁与信号量、原子变量三种方案的对比?

A 阻塞式会浪费 CPU
B 自旋式在长等待时更高效
C volatile/自旋与原子变量是自旋式(不阻塞),锁/信号量是阻塞式(不耗 CPU) ✓ 正确答案
D 三种方案都需要锁
#

13. 手写限流器中令牌桶 vs 滑动窗口的精度、突发与内存复杂度对比?

A 两者精度相同
B 令牌桶内存 O(n)
C 滑动窗口允许突发
D 令牌桶允许突发、内存 O(1);滑动窗口精确但内存 O(窗口请求数) ✓ 正确答案
#

14. 手写 Future(带结果的异步任务)中如何用锁加条件变量保存结果、异常与取消状态?

A get 无需等待即可返回
B 用锁+条件变量管理状态机,setResult 时唤醒、get 阻塞等待、cancel 标记并唤醒 ✓ 正确答案
C 异常无需纳入状态机
D cancel 后 get 返回 result
#

15. 手写线程池中任务队列、worker 线程与拒绝策略的完整实现?

A worker 只执行一次任务
B 任务队列非阻塞
C 用阻塞队列存任务、worker 循环取任务执行,队列满时按拒绝策略处理 ✓ 正确答案
D 拒绝策略只在队列空时触发
#

16. 手写漏斗(leaky bucket)限流中恒定速率出水如何实现,与令牌桶在突发与内存上的差异

A 漏桶与令牌桶完全相同
B 漏桶允许突发超过速率
C 漏桶内存 O(n)
D 漏桶以恒定速率出水、缓冲突发,与令牌桶(允许突发)不同 ✓ 正确答案
#

17. 生产者-消费者中用锁+条件变量与无锁队列(Ring Buffer)两种方式实现?

A 锁式吞吐一定高于无锁
B 无锁 Ring Buffer 无需处理满的情况
C 锁式阻塞省 CPU,无锁 Ring Buffer 用 CAS+原子下标实现高吞吐 ✓ 正确答案
D 无锁 Ring Buffer 需要阻塞
#

18. 手写对象池/连接池中借出、归还与超时回收的状态管理,泄漏如何检测

A 借用集合用于存空闲对象
B 对象借出后无需记录
C 超时回收是多余的
D 维护空闲队列与借用集合,借出/归还切换状态,记录借用时间可检测泄漏 ✓ 正确答案
#

19. 设计多线程任务分发,生产者-消费者加结果聚合如何实现带依赖的任务执行,与线程池 submit 的取舍

A 独立任务用线程池 submit,带依赖任务用依赖计数/回调/DAG 调度 ✓ 正确答案
B 带依赖任务用线程池 submit 即可
C 结果聚合必须用共享变量
D 依赖任务按任意顺序执行