通过Redis实现悲观锁,乐观锁
业务场景:
在高并发的环境下,多个线程去竞争同一个资源,比较常见的有高铁抢票系统,商品秒杀系统等,我们需要保证数据正确,同时系统的吞吐也要尽可能高。
解决方案:
一般多线程同步我们就会想到加锁,用synchornized关键字给并发代码块加锁,但是在我们的业务场景中,比如高铁抢票,有很多张不同的票,但是synchornized锁住了秒杀那个代码块,所有的票全都上了这一把锁,这么看锁的粒度还是太大了,其实我们只需要分别锁住每张票,比如根据票id来上锁,这样锁的粒度就刚刚好。我们可以通过Redis来实现分布式锁,下面分别介绍悲观锁和乐观锁。
Redis乐观锁:
乐观锁顾名思义,就是很乐观,我看到了一件商品库存是1,我觉得这件商品很少人抢,我尝试去下单,当我提交订单的时候,如果商品的库存还是1,那就说明在我之前没人下单,我就可以成功下单,否则的话就下单失败。在写操作较少的情况下,乐观锁是优于悲观锁的,吞吐量会大大提高,但是写操作比较频繁的话就会导致一直重试,如果重试代价较高则不推荐用乐观锁。
Redis的乐观锁主要是通过watch()来实现的,watch()的作用是监视键值对,首先是用multi()开启事务,exec()提交事务,提交事务的时候如果发现键值对的值发生变化则会取消事务,下面是一个测试例子:
/**
* 初始化商品
*/
private static void initProduct() {
// 商品个数
int prdNum = 3;
String key = "prdNum";
String customers = "customers";
Jedis jedis = RedisUtil.getInstance().getJedis();
if(jedis.exists(key)){
jedis.del(key);
}
if(jedis.exists(customers) != null){
jedis.del(customers);
}
jedis.set(key, String.valueOf(prdNum));
RedisUtil.returnResource(jedis);
}
/**
* 顾客抢购商品(秒杀操作)
*/
private static void initClient() throws InterruptedException {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
int num = 10;
for(int i=1; i<=num; i++){
cachedThreadPool.execute(new Customer(i)); //启动与顾客数目相等的线程
}
cachedThreadPool.shutdown();
while(true){
if(cachedThreadPool.isTerminated()){
System.out.println("所有的消费组线程均结束了!");
break;
}
Thread.sleep(100);
}
}
/**
* 打印结果
*/
private static void printResult() {
Jedis jedis = RedisUtil.getInstance().getJedis();
Set
int i = 1;
for (String customer : customers) {
System.out.println("第"+(i++)+"个抢到商品的顾客:"+customer);
}
RedisUtil.returnResource(jedis);
}
static class Customer implements Runnable{
private String name;
private Jedis jedis = null;
private String key = "prdNum";
private String cust = "customers";
public Customer(int name) {
this.name = "编号=" + name;
}
/**
* multi:开启redis事务,置客户端为事务态
* exec:提交事务,执行从multi到此命令之前的命令队列,置客户端为非事务态
* discard:取消事务,置客户端为非事务态
* watch:监视键值对,exec提交事务时发现监视的键值发生变化则取消事务
*/
@Override
public void run() {
// 随机睡眠一下
try {
Thread.sleep((int)Math.random()*5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
while(true){
System.out.println("顾客:" + name + "开始抢购商品!");
jedis = RedisUtil.getInstance().getJedis();
try{
// 监视键值对
jedis.watch(key);
int prdNum = Integer.parseInt(jedis.get(key));
if(prdNum > 0){
// 开启事务
Transaction transaction = jedis.multi();
transaction.set(key, String.valueOf(prdNum - 1));
// 提交事务
List


浙公网安备 33010602011771号