加锁的前提
共享资源被多个线程同时所消费,造成业务逻辑错误
加锁位置
共享资源在哪,就对哪进行加锁
- 如果共享资源在应用上,可以使用JVM自带的本地锁,如synchronized和lock
- 如果在数据库上,使用数据库自带的锁
共享资源在应用上
大家常常看到演示的例子,设个静态变量作为应用共享资源,然后使用JVM本地锁来处理,这是没有任何问题的。
共享资源在Mysql上
实际生产中,共享变量极少是在放在应用上,大部分都是放在数据库中进行保存。如果此时再使用JVM本地锁加锁,就会出现并发问题。举例一段代码:
public void updateStock(){
lock.lock();
try {
Stock stock = this.stockMapper.selectone(new QueryWrapper<Stock>().eq("product_code","1001");
if (stock != null && stock.getCount() > 0){
stock.setcount(stock.getCount() - 1);
this.stockMapper.updateById(stock);
}
}finally {
lock.unlock();
}
}
上面的业务逻辑是:
- 查询库存
- 代码层数量减一
- 更新库存
由于这段业务代码会出现并发问题,因此我们加了JVM本地锁。但在三种情况下,本地锁并不能保证业务安全。
- 锁对象是多例
- 事务
- 集群部署
锁对象多例
这个很好理解,我们说过加锁有个隐藏的前提,锁是同一把。这种其实还挺好避免,实际开发spring默认是单例,只要我们不去瞎改,一般不会此种情况
事务
有人好奇加了事务,咋就突然有数据安全问题呢。先给大家贴下代码:
@Transactional
public void updateStock(){
lock.lock();
try {
Stock stock = this.stockMapper.selectone(new QueryWrapper<Stock>().eq("product_code","1001");
if (stock != null && stock.getCount() > 0){
stock.setcount(stock.getCount() - 1);
this.stockMapper.updateById(stock);
}
}finally {
lock.unlock();
}
//执行后续代码
....
}
给大家