求Java中仅ReentrantLock可实现、synchronized无法完成的场景代码示例
ReentrantLock 可实现但 synchronized 无法完成的手递手锁场景示例
以下是一个典型的**手递手锁(Hand-over-hand Locking)**实现场景:并发双向链表的节点移动操作。这个逻辑只能通过ReentrantLock完成,无法用synchronized实现。
代码实现
带锁的节点类
import java.util.concurrent.locks.ReentrantLock; class Node { int value; Node prev; Node next; final ReentrantLock lock = new ReentrantLock(); // 每个节点自带独立锁 Node(int value) { this.value = value; } }
并发链表的手递手锁操作
class ConcurrentLinkedList { private final Node head = new Node(-1); // 哨兵头节点 // 将指定值的节点移动到链表头部 public void moveToFront(int targetValue) { Node current = head.next; Node prevNode = head; // 先锁定头节点,避免其他线程修改链表头部 head.lock.lock(); try { while (current != null) { // 锁定当前遍历节点 current.lock.lock(); try { if (current.value == targetValue) { // 移除目标节点 prevNode.next = current.next; if (current.next != null) { current.next.prev = prevNode; } // 将目标节点插入头部 current.next = head.next; if (head.next != null) { head.next.prev = current; } head.next = current; current.prev = head; break; } // 关键操作:持有当前节点锁的同时,释放前一个节点的锁(头节点除外) if (prevNode != head) { prevNode.lock.unlock(); } prevNode = current; current = current.next; } finally { // 这里不释放current的锁,下一轮循环会处理,或循环结束后统一释放 } } } finally { // 释放头节点锁 head.lock.unlock(); // 释放最后一个持有锁的非头节点 if (prevNode != head && prevNode != null) { prevNode.lock.unlock(); } } } }
为什么 synchronized 无法实现这个逻辑?
synchronized的锁释放是自动绑定代码块/方法作用域的:当你退出synchronized代码块时,锁会被自动释放,无法主动控制释放时机。
在上述手递手锁逻辑中,核心需求是:
- 先获取节点A的锁,再获取节点B的锁
- 确认B的操作安全后,主动释放A的锁,同时继续持有B的锁
- 接着获取节点C的锁,再释放B的锁,以此类推
如果用synchronized实现,你必须把每个节点的锁操作放在独立的synchronized块中:
- 进入
synchronized(A)块获取A锁,再进入synchronized(B)块获取B锁 - 但要释放A锁,必须先退出
synchronized(A)块,此时你已经不在A的作用域内,后续如果需要操作A的关联节点(比如修改prev/next指针)会出现线程安全问题 - 更关键的是,你无法在持有B锁的同时主动释放A锁——
synchronized没有提供手动释放锁的API,锁的释放完全由JVM控制,无法满足手递手锁的链式操作要求
而ReentrantLock通过lock()和unlock()方法,允许我们在任意时机、任意作用域内主动获取和释放锁,完美适配这种灵活的锁操作顺序。
内容的提问来源于stack exchange,提问作者Deca
相关产品推荐
相关产品推荐

