You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求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代码块时,锁会被自动释放,无法主动控制释放时机。

在上述手递手锁逻辑中,核心需求是:

  1. 先获取节点A的锁,再获取节点B的锁
  2. 确认B的操作安全后,主动释放A的锁,同时继续持有B的锁
  3. 接着获取节点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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 03:54:23