lock与rlock的核心区别在于可重入性:rlock允许同一线程多次acquire并需对应次数release,lock不支持重入,第二次acquire会阻塞;rlock记录持有者线程id,仅该线程可release,lock无此限制。

Lock 和 RLock 都是用来保护临界区的同步原语,但核心区别在于:同一个线程能否多次获取同一把锁。用错会导致死锁,尤其在嵌套调用或递归场景中。
可重入性:同一线程能否重复加锁
RLock 允许同一线程连续调用 acquire() 多次,内部用计数器跟踪加锁次数,只有对应次数的 release() 后锁才真正释放。Lock 不支持重入——第二次 acquire() 会阻塞,哪怕锁就是当前线程刚拿到的。
- 典型死锁场景:函数 A 加了 Lock 后调用函数 B,而 B 内部又尝试加同一把 Lock → 线程卡住,无法继续
- RLock 能自然应对:A 和 B 都用同一把 RLock,不会阻塞,递归调用也安全
持有者标识:谁加的锁、谁才能解
RLock 记录了当前持有锁的线程 ID,只允许该线程调用 release();其他线程调用会抛出 RuntimeError。Lock 没有持有者概念,它只是个“开/关”开关,线程 A 加锁后,线程 B 可以直接 release()(语法合法,但逻辑危险)。
- 这意味着 RLock 更适合封装在类方法中:每个实例方法都独立加锁,又可能相互调用
- Lock 更适合简单共享资源保护,比如多个线程更新一个全局计数器
性能与适用边界
RLock 因需维护线程标识和计数器,比 Lock 多一点开销,性能略低。日常开发中,若没有嵌套、递归或方法间调用加锁的需求,优先选 Lock。
立即学习“Python免费学习笔记(深入)”;
- 选 Lock:生产者消费者模型中的队列操作、多线程累加字典值、单次临界区访问
- 选 RLock:类中多个同步方法互相调用、带锁的递归算法、封装了加锁逻辑的工具类(如带缓存的线程安全对象)
写法兼容但语义不可互换
两者都支持 with 语句和 acquire()/release() 接口,代码替换看似容易,但行为完全不同。把 RLock 替换成 Lock 可能让程序突然卡死;反过来把 Lock 换成 RLock 虽不报错,却掩盖了设计问题(比如本不该嵌套加锁却意外可行)。
- 建议在设计阶段就明确加锁粒度和调用关系,再选型
- 上线前做多线程压力测试,特别关注嵌套路径是否出现超时或无响应










