悲观锁
1、使用场景:
悲观锁可以作为分布式锁的一种实现方式,即你某些业务想在高并发的场景下仍被单机执行时,可以在业务代码执行前,先去获取某行数据的悲观锁,执行业务完成后释放锁(commit or rollback),当你还没有释放锁之前,如果有其他线程执行进来且要获取相同表相同行数据的悲观锁,肯定是获取失败的,会抛出异常,而不会去执行业务代码。
2、悲观锁的核心实现:
select id from table where ... for update nowait
- 此语句如果会把所有符合条件的行锁住,如果没有where条件会锁整张表,如果查询条件获取到的行数据为空,则不会锁住行数据;
- 一旦行数据被这样锁住了,如果获取悲观锁的当前事务还没有commit,那么下一个获取悲观锁的事务一定是失败的;
- 如果锁用完了,需要释放 (即事务的 commit 或 rollback ) 。mybatis框架会自动commit,即在获取完悲观锁以后立即就commit了,而没有等到业务执行完才 commit,在高并发时都能获取悲观锁成功。所以需要使用编称式事务来替代 @Transacional
- nowait表示:不会被阻塞,如果获取悲观锁失败,直接抛异常而不必等待。
3、测试1
测试环境:oracle数据库 + mybatis
释放悲观锁,需要执行 commit 。
4、测试2
5、代码实现
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
TransactionStatus status = null;
try {
//开启编称式事务
status = txManager.getTransaction(new DefaultTransactionDefinition());
//获取悲观锁
funcScheduleDefService.forUpdateNoWait(monthendConfirmDTO.getDid(), function, monthendConfirmDTO.getStatDate());
} catch (Exception e) {
//获取锁失败,回滚事务
txManager.rollback(status);
return null;
}
。。。添加悲观锁成功,执行业务逻辑
//执行业务完成,提交事务,释放锁
txManager.commit(status);