Java 端 synchronized 僅在單 JVM 內有效;叢集、重啟、容錯移轉時會失效。Oracle DBMS_LOCK 釋放機制綁定 Session/事務,應用掛掉 Session 斷開自動釋放,保證跨進程、跨節點串行化。
| API | 用途 |
|---|---|
ALLOCATE_UNIQUE(lockname, lockhandle, expiration_secs) | 邏輯名獲取唯一鎖句柄,多 Session 同名獲取同 handle |
REQUEST(lockhandle, lockmode, timeout, release_on_commit) | 請求鎖,X_MODE 排他鎖,回傳 0 成功、1 逾時、2 死鎖、3 參數錯、4 已持有、5 非法 handle |
RELEASE(lockhandle) | 顯式釋放鎖 |
CREATE OR REPLACE PACKAGE frm_test_task_pkg IS
FUNCTION frm_test_task_func(i_lock_name VARCHAR2, i_procname VARCHAR2, ...) RETURN NUMBER;
END;
CREATE OR REPLACE PACKAGE BODY frm_test_task_pkg IS
PROCEDURE app_task_wrapper_proc(i_procname VARCHAR2) AS
cur INTEGER := DBMS_SQL.OPEN_CURSOR;
BEGIN
DBMS_SQL.PARSE(cur, 'BEGIN ' || i_procname || '; END;', DBMS_SQL.NATIVE);
DBMS_SQL.EXECUTE(cur);
DBMS_SQL.CLOSE_CURSOR(cur);
COMMIT;
END;
FUNCTION frm_test_task_func(...) RETURN NUMBER IS
v_lockhandle VARCHAR2(200); v_result NUMBER;
BEGIN
DBMS_LOCK.ALLOCATE_UNIQUE(i_lock_name, v_lockhandle, i_expiration_time);
v_result := DBMS_LOCK.REQUEST(v_lockhandle, DBMS_LOCK.X_MODE, i_wait_time, TRUE);
IF v_result = 0 THEN app_task_wrapper_proc(i_procname); END IF;
RETURN v_result;
END;
END;
Java 僅呼叫 frm_test_task_func('myLock', 'biz_proc'),動態 SQL 執行業務 SP,參數傳遞較繁瑣。
-- PL/SQL 僅提供鎖操作
CREATE OR REPLACE PACKAGE BODY frm_test_task_pkg IS
PROCEDURE frm_test_lock_acquire(i_lock_name VARCHAR2, ..., o_result OUT NUMBER, o_lockhandle OUT VARCHAR2) IS
BEGIN
DBMS_LOCK.ALLOCATE_UNIQUE(i_lock_name, o_lockhandle, i_expiration_time);
o_result := DBMS_LOCK.REQUEST(o_lockhandle, DBMS_LOCK.X_MODE, i_wait_time, FALSE);
END;
FUNCTION frm_test_lock_release(i_lockhandle VARCHAR2) RETURN NUMBER IS
BEGIN RETURN DBMS_LOCK.RELEASE(i_lockhandle); END;
END;
// Java 模板模式
public abstract class LockedProcedureExecutor {
private final String lockName;
public LockedProcedureExecutor(String lockName) { this.lockName = lockName; }
public final void execute(Connection conn) throws SQLException {
String lockHandle = null;
try {
lockHandle = acquireLock(conn, lockName);
doExecute(conn); // 子類實現具體 SP 呼叫
} finally {
if (lockHandle != null) releaseLock(conn, lockHandle);
}
}
// acquireLock/releaseLock 呼叫 PL/SQL 函數
// Connection 包裝防止子類誤關閉導致鎖未釋放
protected abstract void doExecute(Connection conn) throws SQLException;
}
release_on_commit=FALSE 由 Java 顯式釋放,避免事務提交前釋放導致併發衝突。Connection.close() 僅歸還池,Session 不斷,鎖仍持有;需包裝 Connection 確保 close() 前釋放鎖,或使用模板方法統一控制。DBMS_SQL) 雖通用但難調試、參數綁定繁瑣;建議業務 SP 參數固定化,Java 直接 CallableStatement 呼叫。