springboot使用Redisson实现分布式锁

1.Redisson介绍

Redisson是Redis官方推荐的Java版的Redis客户端。它提供的功能非常多,也非常强大,此处我们只用它的分布式锁功能。

https://github.com/redisson/redisson

2.实现分布式锁

 <dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.15.0</version>
 </dependency>
server:
  port: 8070
spring:
  application:
    name: dkn-provider-store
  jackson:
    default-property-inclusion: non_null
    date-format: YYYY-MM-dd HH:mm:ss
    time-zone: GMT+8
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/dkn-provider-store?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
    username: root
    password: root

logging:
  level:
    com.dkn: debug
    org.springframework.web: trace

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
-- ----------------------------
-- Table structure for store
-- ----------------------------
DROP TABLE IF EXISTS `store`;
CREATE TABLE `store` (
  `id` int(11) NOT NULL COMMENT '商品id',
  `num` int(11) DEFAULT NULL COMMENT '库存数量',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;

-- ----------------------------
-- Records of store
-- ----------------------------
INSERT INTO `store` VALUES ('101', '100');
@Configuration
public class RedissonConfig {
    @Bean
    public RedissonClient redissonClient() {
        Config config = new Config();
        config.useSingleServer().setAddress("redis://127.0.0.1:6379");
        RedissonClient redisson = Redisson.create(config);
        return redisson;
    }

}
@Service
public class StoreServiceImpl extends ServiceImpl<StoreMapper,Store> implements StoreService {

    @Autowired
    RedissonClient redissonClient;

    //没有任何锁
    @Override
    public void buy1(Integer id, Integer num) {
        Store store = this.getById(id);

        //模拟业务处理休息50毫秒
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        store.setNum(store.getNum()-1);
        this.updateById(store);
    }

    //同步
    @Override
    public synchronized void buy2(Integer id, Integer num) {
        Store store = this.getById(id);

        //模拟业务处理休息50毫秒
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        store.setNum(store.getNum()-1);
        this.updateById(store);
    }

    //分布式锁
    @Override
    public void buy3(Integer id, Integer num) {
        String lockKey = "buy:product:" + id;
        RLock lock = redissonClient.getLock(lockKey);
        try {
            boolean res = lock.tryLock(10, TimeUnit.SECONDS);
            if (res) {
                Store store = this.getById(id);

                //模拟业务处理休息50毫秒
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                store.setNum(store.getNum()-1);
                this.updateById(store);
            }
        } catch (Exception ex) {
            log.error("获取所超时!", ex);
        } finally {
            lock.unlock();
        }
    }

}
@RestController
@RequestMapping("/Store")
public class StoreController {

	@Autowired
	private StoreService storeService;

	//模拟购买商品 没有任何锁
	@GetMapping("buy1")
	public AjaxResult buy1(Integer id, Integer num){
		storeService.buy1(id,num);
		return AjaxResult.success();
	}

	//模拟购买商品 synchronized本地同步锁
	@GetMapping("buy2")
	public AjaxResult buy2(Integer id, Integer num){
		storeService.buy2(id,num);
		return AjaxResult.success();
	}

	//模拟购买商品 redisson分布锁
	@GetMapping("buy3")
	public AjaxResult buy3(Integer id, Integer num){
		storeService.buy3(id,num);
		return AjaxResult.success();
	}
	
}

3.测试

@SpringBootTest
@RunWith(SpringRunner.class)
public class StoreBuyTest {

    @Autowired
    RestTemplate restTemplate;

    //测试结果100个商品,库存只减少1个,应该减少50个
    @Test
    public void testBuy1(){
        //模拟50个用户同时购买一件物品
        ThreadPoolExecutor executor=new ThreadPoolExecutor(50,50,60, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());

        for(int i=0;i<50;i++) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("===============================");
                    restTemplate.getForEntity("http://localhost:8070/Store/buy1?id=101&num=1", String.class);
                }
            });
        }

        while (true){
            if(executor.getQueue().size()==0 && executor.getActiveCount()==0){
                executor.shutdown();
                break;
            }

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("end~");
    }

    //单个服务测试-->测试结果正确,测试结果100个商品,库存只减少50个
    @Test
    public void testBuy2_1(){
        //模拟50个用户同时购买一件物品
        ThreadPoolExecutor executor=new ThreadPoolExecutor(50,50,60, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());

        for(int i=0;i<50;i++) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("===============================");
                    restTemplate.getForEntity("http://localhost:8070/Store/buy2?id=101&num=1", String.class);
                }
            });
        }

        while (true){
            if(executor.getQueue().size()==0 && executor.getActiveCount()==0){
                executor.shutdown();
                break;
            }

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("end~");
    }

    //启动2个服务测试,端口分别是8070,8071-->测试结果不正常,测试结果100个商品,库存只减少26个,应该减少50个
    @Test
    public void testBuy2_2(){
        //模拟50个用户同时购买一件物品
        ThreadPoolExecutor executor=new ThreadPoolExecutor(50,50,60, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());

        for(int i=0;i<50;i++) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("===============================");
                    if(RandomUtils.nextInt(0,100)<50){
                        restTemplate.getForEntity("http://localhost:8070/Store/buy2?id=101&num=1", String.class);
                    }else{
                        restTemplate.getForEntity("http://localhost:8071/Store/buy2?id=101&num=1", String.class);
                    }
                }
            });
        }

        while (true){
            if(executor.getQueue().size()==0 && executor.getActiveCount()==0){
                executor.shutdown();
                break;
            }

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("end~");
    }

    //启动2个服务测试,端口分别是8070,8071-->测试结果正确,测试结果100个商品,库存减少50个
    @Test
    public void testBuy3(){
        //模拟50个用户同时购买一件物品
        ThreadPoolExecutor executor=new ThreadPoolExecutor(50,50,60, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());

        for(int i=0;i<50;i++) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("===============================");
                    if(RandomUtils.nextInt(0,100)<50){
                        restTemplate.getForEntity("http://localhost:8070/Store/buy3?id=101&num=1", String.class);
                    }else{
                        restTemplate.getForEntity("http://localhost:8071/Store/buy3?id=101&num=1", String.class);
                    }
                }
            });
        }

        while (true){
            if(executor.getQueue().size()==0 && executor.getActiveCount()==0){
                executor.shutdown();
                break;
            }

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("end~");
    }


}
posted @ 2021-02-20 17:42  dkn  阅读(1349)  评论(0编辑  收藏  举报