我有一个spring方法访问不应该并发运行的数据库(写入和检查可能导致竞争条件)。一个方法可以一次运行,如果方法被多次调用(restapi是主要触发器),第一个示例应该持有锁,执行逻辑并释放锁,然后第二个示例将启动,依此类推。
由于实现分布式锁不是一个简单的任务,我认为必须有一个简单的解决方案,因为这似乎是一个非常普通的sql问题。我找到的解决办法,正是我要找的 sp_getapplock
.
我在dbeaver中使用纯sql脚本对它进行了测试,它可以无缝地工作。第一个调用将获得状态为0的锁(无需等待即可授予),第二个调用将等待并获得状态为1的锁(等待后授予)。
但我无法将逻辑移动并重构为java代码。我尝试从java调用存储过程,但并发调用总是授予状态为0的锁。我错过了什么?我的想法可能是错的吗?我确保代码会在事务中运行。
是否有可能对存储过程的更多调用仍在同一事务中(因为top方法是 @Transactional
)所以我马上去拿锁?如果是这样,在仍然有嵌套事务的情况下是否可以适当地避免这种情况?
我的代码:
sql语句
CREATE PROCEDURE [dbo].[test_lock]
AS
BEGIN
BEGIN TRANSACTION
DECLARE @result INT
EXEC @result = sp_getapplock @Resource = 'TestLock', @LockMode = 'Exclusive', @LockOwner = 'Transaction'
PRINT CONCAT('get - ', @result)
WAITFOR DELAY '00:00:20'
COMMIT TRANSACTION
RETURN @result
END
java
public void testLockSP() throws PersistenceException {
StoredProcedureQuery query = em.createStoredProcedureQuery("test_lock");
query.execute();
System.out.println(query.getFirstResult());
System.out.println();
}
@Test
public void testConcurrent() throws Exception {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
try {
service.testLockSP();
} catch(Exception e) {
e.printStackTrace();
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
service.testLockSP();
} catch(Exception e) {
e.printStackTrace();
}
}
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}
输出:
Hibernate: {call test_lock()}
Hibernate: {call test_lock()}
--waiting--
0
--waiting--
0
暂无答案!
目前还没有任何答案,快来回答吧!