Synchronized 同步出現失效
Synchronized ,大家都知道這個是Java 提供的一種原子性內置鎖,其實現原理是通過獲取對象的監視器monitor進行來實現同步的,只有當線程獲取到對象monitor才能繼續執行,否則該線程進行阻塞(等待)。
示例:
public class DemoServiceImpl {
@Autowired
private DemoDao demoDao;
// 根據id獲取序列號,獲取完之后進行 + 1
@Transactional
public synchronized String getSn(Long id) {
DemoSn sn = demoDao.getSnById(id);
sn.setSn(sn.getSn() + 1);
demoDao.update(sn);
return sn.getSn().toString();
}
}
描述一下代碼示例: 該方法涉及++ 1 ,當如果多個線程進行調用該方法時,會出現數據安全性問題,所以我在這里進行加了 Synchronized, 保證該方法的每次執行只能允許一個線程進來。
本以為這里加了 Synchronized, 就是線程安全了,但是經過測試之后發現,這里同步鎖並沒有起作用,多個線程進行調用該方法時,還是出現返回的 值重復。
Synchronized 失效原因:
先簡單介紹一下Spring @Transactional執行原理: @Transactional 注解是Spring 提供來進行控制事務的注解,當注解標明在方法上時, 則會對 該方法進行做AOP 增強(動態代理),然后在方法執行前,開啟事務,執行后提交事務。
如果上述示例方法只添加了 Synchronized, 並沒有標明@Transactional, 因為加了同步鎖,所以getSn() 一次只能允許一個線程進入, 執行過程:
A(線程): DemoServiceImpl#getSn() > B(線程): DemoServiceImpl#getSn()
如果上述示例方法添加了 Synchronized,並標明@Transactional 線程的執行過程:
A(線程): Spring begins transactional > DemoServiceImpl#getSn() > Spring commits transactional
B(線程): Spring begins transactional > DemoServiceImpl#getSn() > Spring commits transactional
這里看着是沒有問題
開啟事務 -> 執行方法體 -> 提交事務
但是這里也恰恰是出現Synchronized 失效的關鍵
Synchronized 失效關鍵原因:是因為Synchronized鎖定的是當前調用方法對象,而Spring AOP 處理事務會進行生成一個代理對象,並在代理對象執行方法前的事務開啟,方法執行完的事務提交,所以說,事務的開啟和提交並不是在 Synchronized 鎖定的范圍內。出現同步鎖失效的原因是:當A(線程) 執行完getSn()方法,會進行釋放同步鎖,去做提交事務,但在A(線程)還沒有提交完事務之前,B(線程)進行執行getSn() 方法,執行完畢之后和A(線程)一起提交事務, 這時候就會出現線程安全問題。
作者:技術基地
鏈接:https://www.jianshu.com/p/3e30cc705f09
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。