背景描述
应用框架:Spring Boot + mybatis
数据库:Sqlserver
系统一个业务号只允许存在一笔正在处理的预约订单,当多个用户同时发送多笔业务号相同的预约订单,我们发现数据库中也会存在多笔业务号相同的预约订单。而且业务号在表中还不能是主键。
1.原因分析
由于短时间内客户可能连续推送了两条重复的数据,两条数据时间间隔非常小,因此导致了我们的
if(业务号存在){
// ...
}
否则订单入库
if 里面操作还没有执行完毕,第二条拥有相同数据的线程已经进入并通过了if的检验,导致数据库存储了两条相同的数据。
2.解决方法
(1)使用synchronized同步代码块即加同步锁
synchronized(this){
//...
}
这样会导致其他正常数据的推送线程也被阻塞,影响效率。因此不采用。我们考虑只对重复数据加锁。
String lonNbr=... ;
synchronized(lonNbr.intern()) {
//...
}
使用String作为同步锁必须注意产生不同对象的问题,必须保证线程拿到的是同一个String对象,但这个有时很难保证。这个时候就得使用intern()方法,这样就是直接获取的是字符串的值本身,而不是取的String的对象。
Java中String做为synchronized同步锁使用详解
这种加同步锁的方法在负载均衡下的多台应用服务器会失效!
(2)分布式锁
基于缓存(Redis等)实现分布式锁;
基于Zookeeper实现分布式锁;