多线程并发问题
2021-02-05 15:17
标签:tab sele cts 库存 anti 分布 objects spring string 应用场景:库存修改 一:使用 synchronized ,lock 等同步方法: (1)特点:synchronized的flag只有jvm进程内可见,不能跨jvm (2)缺点:1.作用范围是单个jvm实例, 如果做了集群,分布式等,就没用了; 2.数据库的事务隔离级别,加锁时机。主要矛盾是事务开启和提交的时机与加锁解锁时机不一致。 ①Repeatable Read级别:事务开启后,不会读取到其他事务提交的数据. 当T1执行完后,T2执行,读取不到T1提交的数据,所以会出问题。 ②Read Committed级别:事务开启后,可以读取到其他事务提交的数据。 a.开启事务(aop); b.加锁(进入synchronized方法); c.释放锁(退出synchronized方法); d.提交事务(aop). 可以看出是先释放锁,再提交事务.所以T2执行查询,可能还是未读到T1提交的数据,还会出问题。 (3)伪代码: (4)改善: 1.在事务开启前加锁,事务提交后解锁------相当于事务串行化; 2.将查询库存,扣减库存这2步操作,单独提取个方法,单独使用事务,并且事务隔离级别设置为RC; 3.单独的这个方法,需要放到另外的service类中; 4.因为使用spring,同一个bean的内部方法调用,是不会被再次代理的,所以配置的单独事务等需要放到另外的service bean 中。 二:不查询,直接更新 (1)缺点:1.不能跨jvm; 2.不具备通用性,例如add操作。 (2)伪代码: 三:CAS (乐观锁) (1)特点:数据库的事务隔离级别必须是RC。 (2)注意:1.失败重试次数,是否需要限制; 2.失败重试对用户是透明的; 3.CAS中经典问题------ABA的问题(解决:version)。 (3)伪代码: 四:数据库锁(悲观锁 ) (1)特点:1.在查询数据的时候,就将数据锁住.事务串行化; 2.select for update 的flag 是全局可见,可以跨jvm。 (2)原理:1.线程T1 进行sub , 查询库存剩余 100; 2.线程T2 进行sub , 这时候,线程T1事务还未提交,线程T2阻塞,直到线程T1事务提交或回滚才能查询出结果; 3.所以线程T2查询出的一定是最新的数据.相当于事务串行化了,就解决了数据一致性问题。 (3)注意:1.统一入口:所有库存操作都需要统一使用 select for update ,这样才会阻塞, 如果另外一个方法还是普通的select, 是不会被阻塞的; 2.加锁顺序:如果有多个锁,那么加锁顺序要一致,否则会出现死锁。 (4)伪代码: 五:分布式锁(zookeeper,redis等) (1)特点:分布式锁的flag是全局可见,可以跨jvm。 (2)伪代码: 多线程并发问题 标签:tab sele cts 库存 anti 分布 objects spring string 原文地址:https://www.cnblogs.com/BenNiaoXianFei/p/12787673.html 1 public synchronized void buy(String productName, Integer buyQuantity) {
2 Product product = 从数据库查询出记录;
3 if (product.getSurplus buyQuantity) {
4 return "库存不足";
5 }
6 // set新的剩余数量
7 product.setSurplus(product.getSurplus() - quantity);
8 // 更新数据库
9 update(product);
10 // 记录日志...
11 // 其他业务...
12 }
13
1 public synchronized void buy(String productName, Integer buyQuantity) {
2 // 其他校验...
3 int 影响行数 = update table set surplus = (surplus - buyQuantity) where id = 1 and (surplus - buyQuantity) > 0 ;
4 if (result ) {
5 return "库存不足";
6 }
7 // 记录日志...// 其他业务...
8 }
9
1 update t set surplus = 90 ,version = version+1 where id = x and version = oldVersion ;
1 Product product = select * from table where name = productName for update;
2 if (查询的剩余数量 > buyQuantity) {
3 影响行数 = update table set surplus = (surplus - buyQuantity) where name = productName ;
4 } else {
5 return "库存不足";
6 }
1 //获取锁
2 String result = jedis.set(key, value, "NX", "PX", expireMillis);
3 if (result != null && result.equalsIgnoreCase("OK")) {
4 flag = true;
5 }
6
7 //释放锁
8 String script = "if redis.call(‘get‘, KEYS[1]) == ARGV[1] then return redis.call(‘del‘, KEYS[1]) else return 0 end";
9 Object result = jedis.eval(script, Collections.singletonList(fullKey), Collections.singletonList(value));
10 if (Objects.equals(UNLOCK_SUCCESS, result)) {
11 flag = true;
12 }
13
14