超卖问题(多线程安全问题)
超卖问题,即卖出的数量超过了给定的数量,一般是由多线程引起的。
假设此时商品A库存为1件,当多个用户同时进行购买时,同时读到了当前的库存为1,于是都被允许下单,扣减库存,从而使库存为负数,导致超卖。
解决方案
超卖问题是典型的多线程安全问题,针对这一问题的常见解决方案就是加锁:
悲观锁
认为线程安全问题一定会发生,因此在操作数据之前先获取锁,确保线程串行执行。
例如Synchronized、Lock都属于悲观锁。
乐观锁
认为线程安全问题不一定会发生,因此不加锁,只是在更新数据时去判断有没有其它线程对数据做了修改。
如果没有修改则认为是安全的,自己才更新数据。
如果已经被其它线程修改说明发生了安全问题,此时可以重试或异常。
例如在商品买卖的案例中,只需要在扣减库存的SQL语句上稍加修改即可:
此场景下,其实还可以进行优化:
-- 在后面增加判断条件,解决超卖问题
update goods set stock = stock - 1 where id = 1 and stock > 0;
直接判断库存是否大于0,也可以避免超卖问题,且性能更好
总结
悲观锁:添加同步锁,让线程串行执行
- 优点:简单粗暴
- 缺点:性能一般
乐观锁:不加锁,在更新时判断是否有其它线程在修改
- 优点:性能好
- 缺点:存在成功率低的问题