银行 Java 技术面题目整理:锁、volatile 与线程池
- 时间
- 2026-09
- 来源
- 牛客网
《面试题目》
- 乐观锁与悲观锁分别是什么,如何选择?
- synchronized 如何使用,怎样实现同步?
- synchronized 与 Lock 有什么区别?
- volatile 有什么作用,为什么不能让自增操作整体成为原子操作?
- 线程池解决什么问题,任务如何在线程和队列之间分配?
- Java 原子类有哪些,如何完成并发更新?
《参考解析》
版本号要和更新放在同一条语句里
乐观并发控制的关键是校验自己读到的版本仍然有效。例如读取 version=7 后,更新时使用 WHERE id=? AND version=7,并在成功更新时把 version 加一。受影响行数为零,就代表记录不存在或发生了并发修改,需要重新读取并决定是否重试。
不能只比较提交的新版本是否“比数据库版本大”,也不能先查版本、再无条件写入。悲观锁则先获得相应资源的锁,再完成受保护的操作。选型要看冲突程度、临界区耗时和失败后的重试成本。
volatile 解决的不是整段代码互斥
对 volatile 字段的写,与随后对同一字段的读之间有可见性和顺序保证,但 count++ 仍包含读、加一、写回多个动作,两个线程可能从同一个旧值出发。计数可使用 AtomicInteger 的原子增量操作;多个字段之间存在业务约束时,要保护整个约束。参见 Java 并发包的内存一致性说明。
锁的释放路径不能漏
synchronized 在退出同步块时释放监视器。ReentrantLock 则需要在成功加锁后,用 finally 调用 unlock;它还支持可中断获取、限时尝试和多个条件队列。两者都是可重入机制,但不能脱离竞争情况和运行环境,给出谁一定更快的结论。
线程池容量要包含排队任务
只限制工作线程数,不能限制所有待处理任务占用的内存。无界队列在持续过载时会越积越长;有界队列配合明确的拒绝处理,才能把过载反馈给提交方。拒绝也可能由线程池已关闭触发,不能把某种线程池描述成“永远不会拒绝”。具体调度规则可核对 ThreadPoolExecutor 文档。