結論:如果在service層的方法上同時使用事務和同步鎖無法保證數據同步。
1 @Service 2 public class ServiceImpl{ 3 4 private static Lock lock = new ReentrantLock(false); 5 6 @Transactional(rollbackFor = Exception.class) 7 public void update() { 8 try { 9 lock.lock(); 10 ... ... 11 } catch (Exception e) { 12 e.printStackTrace(); 13 } finally { 14 lock.unlock(); 15 } 16 } 17 }
上面這個例子無法保證數據的一致性,synchronized 同理。
原因:
根據spring的AOP的特性,會在update方法之前開啟事務,之后再加鎖,當鎖住的代碼執行完成后,再提交事務。
由於lock代碼塊執行是在事務之內執行的,在代碼塊執行完時,事務還未提交,因此其它線程進入synchronized代碼塊后,讀取的數據庫數據不是最新的(臟讀)。
解決方案:
1.在還沒有開啟事務之前就加同步鎖,用加鎖的方法調用加事務的方法
1 @Service 2 public class ServiceImpl{ 3 4 private static Lock lock = new ReentrantLock(false); 5 6 public void update1() { 7 try { 8 lock.lock(); 9 update2(); 10 } catch (Exception e) { 11 e.printStackTrace(); 12 } finally { 13 lock.unlock(); 14 } 15 } 16 17 @Transactional(rollbackFor = Exception.class) 18 public void uodate2() { 19 ... ... 20 } 21 }
2.把鎖放到上一層
1 @Controller 2 public class TestController{ 3 @Autowired 4 private IServiceImpl serviceImpl; 5 6 private static Lock lock = new ReentrantLock(false); 7 8 public String test() { 9 try { 10 lock.lock(); 11 serviceImpl.update(); 12 } catch (Exception e) { 13 e.printStackTrace(); 14 } finally { 15 lock.unlock(); 16 } 17 } 18 } 19 20 @Service 21 public class ServiceImpl{ 22 23 @Transactional(rollbackFor = Exception.class) 24 public void update() { 25 ... ... 26 } 27 }
