redis 实现分布式锁

1.初始化

public class RedisLock {	
    static Jedis jedis = new Jedis("127.0.0.1", 6379);
	static String kill_key = "goods-1-1-1-num";
	static String lock_key = "lock-goods-1-1-1";

    public static void init() {
		jedis.set(kill_key, "500");
		String num = jedis.get(kill_key);
		System.out.println(num);
	}
}

2.setnx 和expire

  • 通过 jedis.setnx(lock_key, "lock") 的返回值来判断是否获取锁(即能不能写入值),返回1即获取锁,返回0则失败
  • 为了避免宕机而没有释放锁,给锁设置存活时间
  • 下面代码中 System.exit(10); 模拟了宕机,finally 里的释放锁代码没有执行,可通过注释并执行两次main 函数来验证
public class RedisLock {
	static Jedis jedis = new Jedis("127.0.0.1", 6379);
	static String kill_key = "goods-1-1-1-num";
	static String lock_key = "lock-goods-1-1-1";
	public static void main(String[] args) throws Exception {
		jedis.del(lock_key);
		demo1();

        //第二次注释掉上面两句代码,打印下面代码得到0,说明不能获取锁
		//System.out.println(jedis.setnx(lock_key, "lock"));
	}
	
	public static void demo1() {
		if (jedis.setnx(lock_key, "lock") == 1) {
			System.exit(10); // 退出虚拟机模拟宕机。。
			jedis.expire(lock_key, 5);
			try {
				// do something
			} finally {
				jedis.del(lock_key);
			}
		}
	}
}

3.通过 jedis.set(lock_key, "lock", "nx", "ex", 5) 保证原子性来解决2 中的问题

  • 第三个参数:只能填 nx(不存在则赋值) 或 xx(存在则赋值)
  • 第四个参数:ex(秒)或 px(毫秒)
        if ("ok".equals(jedis.set(lock_key, "lock", "nx", "ex", 5))) {
			try {
				// do something
			} finally {
				jedis.del(lock_key);
			}
		}

4.时间问题

  • 规定时间内线程 A 的任务还没完成,而锁过期则自动释放了
  • 此时线程 B 则获取了锁,此时两个线程同时执行任务
  • 线程 A 完成任务,执行finally 把 B 的锁给释放了

用Lua 脚本。。。暂时不会。。。

 

5.使用守护线程

  • 守护线程中设置一定时间后执行判断 key 的剩余的存活时间,若能执行判断说明当前的执行业务的线程还没结束,而且 key 的存活时间剩余不多了,可以在此时为key “续命”,当然可以隔一段时间判断。
  • 下面例子设置 key 的生存时间为15秒,若过了10秒剩余5秒还没完成的时候,守护线程会为 key 续命为10秒。
public class RedisLock {
	Jedis jedis = new Jedis("127.0.0.1", 6379);
	String kill_key = "goods-1-1-1-num";
	String lock_key = "lock-goods-1-1-1";

	@Test
	public void main() {

		doSomething();
	}
	@Test
	public void test() {
		Long ttl = jedis.ttl(lock_key);
		System.out.println(ttl);
	}

	public void doSomething() {
		String isOk = jedis.set(lock_key, "lock", "nx", "ex", 15);
		System.out.println(isOk);
		// 给key设置了15秒的超时时间,而线程20秒才执行完
        if ("OK".equals(isOk)) {
			try {
				createDaemon().start();
				// do something over time
				Thread.sleep(20000); // 假设超过15秒还没执行完
			} catch (Exception e) {
				e.printStackTrace();
			} 
			finally {
				jedis.del(lock_key);
			}
		}
	}

	public Thread createDaemon() {
		Thread daemon = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					System.out.println("开始执行");
					// 延迟10秒,若还能有判断,则程序还没执行完,finally 还没运行
					Thread.sleep(10000);
					System.out.println("睡完");
					Long ttl = jedis.ttl(lock_key);
					System.out.println(ttl);
					if ( ttl <= 5 && ttl > 0) {
						jedis.expire(lock_key, 10);						
						System.out.println("续命成功");
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
		daemon.setDaemon(true);
		return daemon;
	}
}

 

### 使用 Redis 实现分布式锁的最佳实践 Redis 是一种高性能的内存数据库,由于其出色的性能和易于操作的特性,被广泛用于实现分布式锁。以下是使用 Redis 实现分布式锁的最佳实践以及代码示例。 #### 1. 高可用性 为了确保分布式锁在高可用环境下正常运行,可以采用 Redlock 算法,在多个独立的 Redis 实例上获取锁[^1]。此外,可以通过 Redis Sentinel 或 Redis Cluster 提供更高的可用性和容错能力[^2]。 #### 2. 锁的实现方式 Redis 分布式锁的基本实现依赖于 `SET` 命令,并设置过期时间以防止死锁的发生。以下是一个基本的实现方式[^3]: - 使用 `SET key value NX PX timeout` 命令来尝试获取锁。 - 如果命令成功执行,则表示获取锁成功;否则,表示锁已被其他客户端持有。 #### 3. 自动续期机制 为了避免锁因超时而被误释放,可以引入自动续期机制。例如,Redisson 提供了看门狗功能,通过定期延长锁的有效期来避免锁死的情况[^4]。如果需要手动实现类似功能,可以在加锁时记录锁的创建时间和有效期,并在有效期内定期更新锁的过期时间。 #### 4. 可重入锁 如果业务场景需要支持可重入锁(即同一个线程可以多次获取同一把锁),可以使用 Redisson 的 RLock 对象,它实现了 Java 的 `java.util.concurrent.locks.Lock` 接口。RLock 支持可重入锁的功能,并且可以通过 `tryLock()` 方法指定锁的等待时间和锁的持有时间。 #### 5. 锁的释放 释放锁时需要确保只有加锁的客户端才能解锁,通常的做法是将客户端唯一标识作为锁的值存储在 Redis 中。解锁时,先检查锁的值是否与当前客户端的标识一致,然后删除锁。 --- ### 代码示例 以下是一个基于 Python 和 Redis分布式锁实现: ```python import redis import time import uuid class RedisDistributedLock: def __init__(self, redis_client, lock_key, expire_time=10000): self.redis_client = redis_client self.lock_key = lock_key self.expire_time = expire_time self.client_id = str(uuid.uuid4()) def acquire_lock(self): while True: if self.redis_client.set(self.lock_key, self.client_id, nx=True, px=self.expire_time): return True time.sleep(0.1) def release_lock(self): script = """ if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end """ self.redis_client.eval(script, 1, self.lock_key, self.client_id) # 示例使用 redis_client = redis.StrictRedis(host='localhost', port=6379, db=0) lock = RedisDistributedLock(redis_client, "my_distributed_lock") if lock.acquire_lock(): try: print("锁已获取,执行关键操作...") # 执行业务逻辑 finally: lock.release_lock() print("锁已释放") ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值