解决超发现象的方法(有两种方法)
1、悲观锁
悲观锁是一种利用数据库内部机制提供的锁的方法,也就是对更新的数据加锁,这样在并发期间一旦有一个事务持有了数据库记录的锁,其他的线程将不能再对数据进行更新了。
修改SQL语句,加入“for update”,意味着将持有对数据库记录的行更新锁(因为这里使用主键查询,所以只会对行加锁。如果使用的是非主键查询,要考虑是否对全表加锁的问题,加锁后可能引发其他查询的阻塞),那就意味着在高并发的场景下,当一条事务持有了这个更新锁后才能往下操作,其他的线程如果要更新这条记录都需要等待,这样就不会出现超发现象引发的数据不一致的问题了。
但是,悲观锁会导致系统性能下降。对于悲观锁来说,当一条线程抢占了资源后,其他的线程将得不到资源,那么这个时候,CPU 就会将这些得不到资源的线程挂起,挂起的线程也会消耗 CPU 的资源。由于高并发环境下的频繁挂起线程和恢复线程,导致CPU频繁切换线程上下文,从而使CPU资源得到了极大的消耗,造成了性能不佳的问题。悲观锁也称独占锁。
1)、具体步骤如下:
(1)、环境配置、业务逻辑和“抢红包超发现象一致”,只是在它基础上加上一些语句和语法
(2)、RedPacket.xml
<!-- 查询红包具体信息 加锁语句 -->
<select id="getRedPacketForUpdate" parameterType="long"
resultType="com.ssm.chapter22.pojo.RedPacket">
select id, user_id as userId, amount, send_date as
sendDate, total,
unit_amount as unitAmount, stock, version, note
from
T_RED_PACKET where id = #{id} for update
</select>
(3)、DAO层(RedPacket.java)
/***
* 使用for update语句加锁.
* @param id ——红包id
* @return 红包信息
*/
public RedPacket getRedPacketForUpdate(Long id);
(4)、Service层(RedPacketService.java接口)
/***
* 使用for update语句加锁.
* @param id ——红包id
* @return 红包信息
*/
public RedPacket getRedPacketForUpdate(Long id);
(5)、接口实现类RedPacketServiceImpl.java
@Override
@Transactional(isolation=Isolation.READ_COMMITTED,propagation=Propagation.REQUIRED)
public RedPacket getRedPacketForUpdate(Long id) {
return redPacketDao.getRedPacketForUpdate(id);
}
(6)、接口实现类UserRedPacketServiceImpl.java
在UserRedPacketServiceImpl.java里grapRedPacket()方法中,
把RedPacket redPacket = redPacketDao.getRedPacket(redPacketId);改为
RedPacket redPacket=redPacketDao.getRedPa