今天在用jmeter進行壓力測試的時候,出現了併發問題,代碼如下:
@Override
@Transactional
public synchronized CouponsH5Respons getACoupons() {
.....
}
當時很疑惑,synchronized已經放在了方法級別,不應該出現併發的情況,於是我用IDEA的條件斷點,一路跟下去,發現了重複讀的問題(數據庫是Mysql,默認引擎innobd ,默認的隔離級別是可重複讀).
可重複讀發生的場景是什麼:
在同一個事務內,多次執行同一查詢,結果一定是相同的,就算期間這條數據被更改,讀取的結果也一致
那爲什麼上邊代碼併發的情況下會出現兩個線程查詢的結果是相同的呢?T1修改了數據T2應該能讀取到纔對.
原因:
由於spring的AOP的特性,會在進入方法之前開啓事務,之後再加鎖,當鎖住的代碼執行完成後,在提交事務。
因此synchronized代碼塊執行是在事務之內執行的,可以推斷在代碼塊執行完時,事務還未提交,因此其它線程進入synchronized代碼塊後,發生了 2個線程在同一個事務中的可重複讀問題。
如下圖:
同步代碼塊是在事務的內部,
T1 進入synchronized之前開啓事務,執行完代碼還未提交事務,這時候T2進來了,出現了T1 T2 在同一個事務下,也就發生了mysql 重複讀的問題
解決方式一:目的是讓同步代碼在事務的外面
@Override
public synchronized CouponsH5Respons getACoupons() {
test();
}
@Transactional
public CouponsH5Respons test() {
.....
}
變成了如下圖: