示例代碼
@Transactional
public void update(int id) {
boolean lock = redisLock.lock(id);
if (!lock) {
throw new RuntimeException("當前人數過多,請稍后再試");
}
/*
業務代碼在該區域
*/
redisLock.unlock(id);
}
在上面的代碼中,我們同時使用了@transactional和redis分布式鎖(其他鎖同理,比如synchronized同步鎖也會出現這個問題)
問題分析
上面這個例子是無法保證數據的一致性.由於spring的aop,會在update方法之前開啟事務,之后再加鎖,當鎖住的代碼執行完成后,再提交事務,因此鎖住的代碼塊執行是在事務之內執行的,可以推斷在代碼塊執行完時,事務還未提交,鎖已經被釋放,此時其他線程拿到鎖之后進行鎖住的代碼塊,讀取的庫存數據不是最新的。
解決方法
我們可以在update方法之前就加上鎖,在還沒有開事務之前就加鎖,那么就可以保證線程的安全性,從而不會出現臟讀和數據不一致性等情況.
@RequestMapping("/execute")
public void execute(int id) {
boolean lock = redisLock.lock(id);
if (!lock) {
throw new RuntimeException("當前人數過多,請稍后再試");
}
service.update(id);
redisLock.unlock(id);
}
@Transactional
public void update(int id) {
/*
業務代碼在該區域
*/
}
