多线程并发争用问题

多线程并发争用问题场景
单体架构,只剩一个号源,多个用户同时挂号,怎么保证这个号源只被一个用户挂成功?

可以使用,同步关键字 synchronized,或者加锁 lock。

这个只适用于同一个JVM中,分布式不适用。分布式项目中,每个服务都有自己的JVM。

分布式架构,还剩10个号,100个用户同时请求,怎么保证数据只被10个人请求成功?

1)可以使用Redis的分布式锁 setnx;

2)命令语法如下:setnx key value;效果等同于 SET key value NX

3)代码表现形式如下:返回的是一个布尔值,true or false;Boolean isSuccess = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "1");

4)添加锁后处理逻辑:

 // 加分布式锁: setnx

Boolean isSuccess = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, "1");

isSuccess==true,抢锁成功的线程,继续后续业务处理。

比如:预约挂号项目升级为分布式架构,专家号一般只有少量,一百多万人同时预约专家号。此时可以添加Redis的分布式锁,对抢专家号源成功的线程,可进行正常的预约业务处理,并扣减专家号剩余号源数;

isSuccess==false,抢锁失败的线程,可以暂停20s进行递归重试。

最后记得在finally释放锁。

加锁
1、分布式架构-加分布式锁代码:
@RestController
public class TestController {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Value("${server.port}")
    private String port;

    /**
     * @description 分布式线程抢券、抢单
     * @author 邓林妹
     * @date 2024/3/5 21:19
     */
    @RequestMapping("/sale")
    public String sale() {
        String message = "";
        String key = "redisLock";
        //uuidValue==uuid:线程id
        String uuidValue = UUID.randomUUID().toString().replace("-", "") + ":" + Thread.currentThread().getId();

        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, uuidValue);
        //flag=false,抢不到的线程要继续重试
        if (flag == null || !flag) {
            //暂停20s,进行递归重试
            try {
                TimeUnit.MILLISECONDS.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            //抢锁成功的线程,进行正常的业务逻辑处理,扣减库存
            try {
                //1、查询库存信息
                String result = stringRedisTemplate.opsForValue().get("inventory001");
                //2、查询库存是否充足
                Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);
                //3、扣减库存
                if (inventoryNumber > 0) {
                    stringRedisTemplate.opsForValue().set("inventory001", String.valueOf(--inventoryNumber));
                    message = "成功卖出一个商品,库存剩余:" + inventoryNumber;
                    System.out.println(message + "\t" + "服务端口" + port);
                } else {
                    message = "商品卖完了";
                }
            } finally {
                //释放锁
                stringRedisTemplate.delete(key);
            }
        }
        return message;
    }
2、单体架构-线程同步、加锁代码:
@RestController
public class TestController {

    private Lock lock = new ReentrantLock();

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Value("${server.port}")
    private String port;
    
    //方法一:synchronized
    public synchronized String sale2() {
      //业务处理代码
      return null;
    }
    //方法二:lock
    public String sale() {
        String message = "";

        lock.lock();
        try {
            //1、查询库存信息
            String result = stringRedisTemplate.opsForValue().get("inventory001");
            //2、查询库存是否充足
            Integer inventoryNumber = result == null ? 0 : Integer.parseInt(result);
            //3、扣减库存
            if (inventoryNumber > 0) {
                stringRedisTemplate.opsForValue().set("inventory001", String.valueOf(--inventoryNumber));
                message = "成功卖出一个商品,库存剩余:" + inventoryNumber;
                System.out.println(message + "\t" + "服务端口" + port);
            } else {
                message = "商品卖完了";
            }
        } finally {
            lock.unlock();
        }
        return message + "\t" + "服务端口" + port;
    }
其它说明

使用 StringRedisTemplate 类操作 redis 时,需要引入的maven依赖。

        <!--***** SpringBoot 整合 redis start ****-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!--**spring-boot-starter-data-redis 默认采用 lettuce作为redis客户端,
        lettuce 底层采用 netty实现, 可以在多个线程中并发访问,且线程安全,
        在使用lettuce需要配置线程池**-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>
        <!--***** SpringBoot 整合 redis end ****-->
  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值