实现 Redis 完成分布式锁 所用到的指令 :
- setnx : 如果key值不存在进行设置并返回 1,如果 key值存在不进行任何操作并返回 0
- expire : 给key 值设置过期时间
- del : 删除所对应的 key
思路:
1 . 在执行具体的买票业务之前先通过 setnx 的指令去获取其返回值,如果设置成功(返回值为1) 说明获取到了锁,没有设置成功(返回值为0),则说明没有获取到锁,继续循环执行
2. 抢到锁的线程先给key 设置过期时间,这一步主要是为了避免死锁问题(在获取锁之后释放锁之前,如果程序出现异常,则不会释放锁,就会出现死锁问题)
3. 执行具体的业务逻辑代码
4. 释放锁(删除 key )
具体代码的实现:
package com.tk;
import redis.clients.jedis.Jedis;
/**
* #Description : // 使用Redis 实现分布式锁
* #Date: 2021/1/20 19:19
* @author : tiankun
*/
public class RedisDistributedLock {
public static void main(String[] args) {
// 模拟三个黄牛买票
new Thread(new People(),"黄牛1").start();
new Thread(new People(),"黄牛2").start();
new Thread(new People(),"黄牛3").start();
}
}
/**
* 模仿人买票
*/
class People implements Runnable{
private static int ticketCount = 10; // 总票数
@Override
public void run() {
Jedis jedis = new Jedis("192.168.112.134", 6379);
// 1. 获取锁
Long lock = 0L;
while (ticketCount > 0){
while (true){
// 模拟买票的间隔时间
// 注意:这个模拟买票的间隔时间必需写在逻辑代码前面,这样才能保证每个黄牛都会具有相同的间隔时间,如果放在最后,抢到锁的线程就不会sleep,会出现总是一个人抢到锁的情况
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 当没有票的时候让其他黄牛退出死循环
if(ticketCount < 1){
System.out.println("没有票了");
break;
}
// 如果获取到锁继续执行业务逻辑(购买票)
lock = jedis.setnx("ticket","true");
System.out.println(Thread.currentThread().getName()+" ==> "+lock);
if(lock == 1){
// 2. 给锁设置过期时间避免发生死锁问题
jedis.expire("ticket",2);
break;
}
}
// 防止最后一张票被重复购买的问题
if(ticketCount < 1){
System.out.println("没有票了");
break;
}
// 3.执行购买票业务逻辑
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
buyTicket();
// 4. 释放锁
jedis.del("ticket");
}
}
/**
* 购买票
*/
private void buyTicket(){
try {
// 模拟网络延迟
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"购买了票,剩余的票数为"+(--ticketCount));
}
}