概念定义
线程调用加锁的方法 A,A 又调用同样加锁的方法 B:普通锁下线程永远等自己;可重入锁识别属主并成功,按深度计数直到配对的释放逐层解开。
工作机制
锁记录属主身份与递归计数:属主获取自增、解锁自减、归零才真正释放给他人。公平性、带超时的 try-lock、条件变量是 ReentrantLock 这类库实现的附加能力。
使用场景
受保护方法互相调用的公共 API、在锁定区调用用户覆写的框架、以及遗留调用形态的兼容。可选时应收窄锁范围,让重入变得不必要。
常见误区
可重入只防单线程自死锁,多线程环照死不误——加锁顺序纪律仍然必须。它还会把“意外的递归加锁”静默掉:代码看似正常,性能与不变量却在退化。循环里多次获取只释放一次会腐蚀计数;条件等待期间的锁释放与深度记账交互也常出意外。重入不是免性能的——每次获取释放都多付簿记。
代码示例
-- PostgreSQL 会话级咨询锁:try 锁拿不到立即返回假
SELECT pg_try_advisory_lock(4271);
-- 若返回 true,此会话独占编号 4271 的锁
-- 其他实例的尝试会得到 false:
SELECT pg_try_advisory_lock(4271); -- false:已被占用
-- 临界区:只有一个实例在执行对账
BEGIN;
SELECT pg_advisory_xact_lock(4271); -- 事务级:提交或回滚自动释放
UPDATE reconciliation_runs
SET started_at = now(), status = 'running'
WHERE id = 'nightly-2026-10-21';
-- ... 对账主体 ...
UPDATE reconciliation_runs
SET finished_at = now(), status = 'done'
WHERE id = 'nightly-2026-10-21';
COMMIT; -- 锁随事务结束自动归还
-- 会话级锁的显式释放路径
SELECT pg_advisory_unlock(4271);
-- 双键形式:类目 ID 加实体 ID 组合编号
SELECT pg_try_advisory_lock(4271, 42);
-- 锁状态观测
SELECT pg_locks.locktype, pg_locks.objid, pg_locks.granted
FROM pg_locks
WHERE locktype = 'advisory';
#!/usr/bin/env bash
set -euo pipefail
lock_file=${1:-"${TMPDIR:-/tmp}/devexamples-backup.lock"}
max_wait=${LOCK_WAIT_SECONDS:-5}
work_file=""
cleanup() {
local status=$?
if [[ -n "$work_file" && -f "$work_file" ]]; then
rm -f -- "$work_file"
fi
flock -u 9 2>/dev/null || true
exit "$status"
}
trap cleanup EXIT
exec 9>"$lock_file"
if ! flock -w "$max_wait" 9; then
printf 'could not acquire %s within %ss\n' "$lock_file" "$max_wait" >&2
exit 75
fi
printf 'lock acquired by pid %d\n' "$"
work_file="$(mktemp "${TMPDIR:-/tmp}/backup.XXXXXX")"
printf 'job %d started at %s\n' "$" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" > "$work_file"
sleep "${JOB_SECONDS:-1}"
cat -- "$work_file"
printf 'job %d finished; lock released on exit\n' "$"