reentrantlock 不直接构建任务队列,而是为自定义队列提供线程安全保障;需结合阻塞队列、锁粒度控制、condition 等实现高并发队列,支持中断、超时、公平性及多条件变量。

ReentrantLock 本身不直接构建任务队列,而是为自定义任务队列提供线程安全的底层保障。真正高并发的任务队列需要结合阻塞队列(如 ArrayBlockingQueue 或 LinkedBlockingQueue)、锁粒度控制、条件等待机制和合理的拒绝策略来实现。ReentrantLock 的优势在于可中断、可超时、支持公平性及多条件变量——这些特性让任务入队、出队、动态扩缩容等操作更可控。
用 ReentrantLock + Condition 实现带等待通知的任务队列
标准阻塞队列已内置锁机制,但若需定制行为(如优先级调度、按标签分组消费、或与外部状态联动),可基于 ReentrantLock 和 Condition 手动实现:
- 使用一个
ReentrantLock保护共享任务列表(如ArrayList<runnable></runnable>) - 声明两个
Condition:一个用于“非空”(有任务可取),一个用于“非满”(有空间可加) - 入队时先
lock.lock(),检查容量,不满则添加并notFull.signal();否则notFull.await() - 出队时同样加锁,检查是否为空,不空则取出并
notEmpty.signal();否则notEmpty.await() - 务必在
finally块中调用unlock(),避免死锁
避免锁争用:读写分离或分段锁设计
单锁在超高并发下会成为瓶颈。可通过以下方式降低竞争:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 将任务队列拆分为多个子队列(如按哈希取模分配),每个子队列配独立
ReentrantLock - 对只读操作(如查看队列长度、遍历待处理任务)使用
StampedLock的乐观读,减少写锁持有时间 - 入队和出队使用不同锁(如生产者锁 + 消费者锁),但需额外同步状态(如原子计数器)防止脏读
- 慎用公平锁(
new ReentrantLock(true)),它虽保证FIFO,但会显著降低吞吐量
支持中断与超时的健壮任务提交
相比 synchronized,ReentrantLock 允许响应中断和设置超时,这对长时等待场景至关重要:
- 调用
lockInterruptibly()替代lock(),使阻塞线程能被Thread.interrupt()唤醒 - 使用
tryLock(long timeout, TimeUnit unit)避免无限等待,超时后可降级(如丢弃、重试、记录告警) -
awaitNanos(long nanosTimeout)或awaitUntil(Deadline)可精确控制等待时长 - 注意:中断状态需主动检查(如
Thread.interrupted()),并在 catchInterruptedException后恢复或传播
与线程池协同:作为外部任务缓冲区
将自定义锁队列作为 ThreadPoolExecutor 的工作队列,需实现 BlockingQueue 接口:
- 覆盖
offer()、poll()、take()、put()等核心方法,内部用ReentrantLock保证线程安全 - 在
take()中使用notEmpty.await()实现阻塞获取;在put()中用notFull.await()控制容量 - 重写
size()和remainingCapacity()时注意锁范围,避免长时间持锁影响性能 - 构造
ThreadPoolExecutor时传入该队列,并搭配合适的拒绝策略(如AbortPolicy或自定义策略)
不复杂但容易忽略:锁只是工具,关键在如何划分临界区、何时释放、如何配合条件变量做精准唤醒。任务队列的性能瓶颈往往不在锁本身,而在内存可见性、GC压力、或任务执行耗时导致的消费者积压。建议先用标准 LinkedBlockingQueue 或 ConcurrentLinkedQueue 验证业务逻辑,再根据压测结果决定是否引入定制锁队列。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










