springboot使用redisson实现分布式锁

一、前言
在实际项目中,某些场景下可能需要使用到分布式锁功能,那么实现分布式锁有多种方式,常见的如mysql分布式锁、zookeeper分布式锁、redis分布式锁,从效率上讲,redis无疑是性能最好的,但也会存在一些问题

1.获取锁的线程在执行任务的过程中挂掉,来不及释放锁,这块资源将会永远被锁住(死锁),别的线程再也别想进来,因此我们需要给key加个过期时间,保证这把锁要在一定时间后自动释放。

2.高并发情况下redis分布式锁永久失效 的问题(一个线程可能删除了别的线程的锁)
假设线程 A 可能某些原因执行的很慢很慢,到达过期时间都没执行完,这时候锁过期自动释放,此时线程 B 得到了锁;

随后,线程 A 执行完了任务,线程 A 随之释放锁。但这时候线程 B 还没执行完,线程A实际上 删除的是线程 B加的锁
解决方法:
可以在 释放锁之前做一个判断,验证当前的锁是不是自己加的锁

3.可能出现并发情况
当线程 A 执行的很慢很慢,到达过期时间都没执行完,这时候锁过期自动释放,线程 B得到了锁。此时就有多个线程在访问同步代码块。
解决方法:我们可以使用redisson实现,内部实现锁续期功能。

二、实现
1.在springboot中引入redisson依赖包

 
    		org.redisson
    		redisson-spring-boot-starter
    		3.10.6
		

2.配置redisson,代码如下:

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

@Configuration
public class RedissonConfig {
	
	@Bean(destroyMethod = "shutdown")
	public RedissonClient redisson() throws IOException{
		//RedissonClient redisson = Redisson.create(Config.fromYAML(new //ClassPathResource("redisson-single.yml").getInputStream()));
		Config config = new Config();
        config.useSingleServer()
        .setAddress("redis://192.168.6.52:6379").setPassword("123456")
        .setRetryInterval(5000)
        .setTimeout(10000)
        .setConnectTimeout(10000);
        return Redisson.create(config);
   }
}

3.我们以商品库存为例:

import java.util.concurrent.TimeUnit;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.suntree.entity.Price;
import com.suntree.mapper.PriceMapper;

@Service
public class ProductService {
	@Autowired
	RedissonClient redissonClient;
	@Autowired
	ProductMapper productMapper;
	
	@Transactional
	public String descreaseProduct(String productId,Integer quanlity) {
		String key="des_product_lock:"+productId;
		RLock lock=redissonClient.getLock(key);
		lock.lock();
		Product product =productMapper.selectById(productId);
		if(product ==null) {
			return "产品未找到";
		}
		
		String result="";
		try {
			if(product .getQuanlity()==0) {
				return "当前数量为0,不能再扣了!!";
			}
			product .setQuanlity(product .getQuanlity()-1);
			productMapper.updateById(product );
			result = "当前数量:" + product .getQuanlity();
			System.err.println(result);
		}catch (Exception e) {
			System.err.println(e.getMessage());
			throw new RuntimeException("扣库存操作失败了");
		}finally {
			lock.unlock();
		}
		return result;
	}
}

这就是实现分布式锁常见场景。

4.接着我们可以在controller层调用,我们可以模拟多线程操作,看扣库存是否会有问题。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.suntree.service.ProductService ;

@RestController
@RequestMapping("/product")
public class ProductController {
	@Autowired
	ProductService productService ;
	
	@GetMapping("/descrease")
	public String descreaseProduct() {
		return productService .descreaseProduct("2020-2020", 200);
	}
}

大家可以自己尝试下。

你可能感兴趣的:(spring,boot,分布式,java)