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;
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值