利用redis可以实现分布式锁,demo如下:
1 /** 2 * 保存每个线程独有的token 3 */ 4 private static ThreadLocal<String> tokenMap = new ThreadLocal<>(); 5 6 /** 7 * redis实现分布式可重入锁,并不保证在过期时间内完成锁定内的任务,需根据业务逻辑合理分配seconds 8 * 9 * @param lock 10 * 锁的名称 11 * @param seconds 12 * 锁定时间,单位 秒 13 * token 14 * 对于同一个lock,相同的token可以再次获取该锁,不相同的token线程需等待到unlock之后才能获取 15 * @return 16 */ 17 public boolean lock(final String lock,final int seconds) { 18 // @param token 对于同一个lock,相同的token可以再次获取该锁,不相同的token线程需等待到unlock之后才能获取 19 String token = tokenMap.get(); 20 if (StringUtil.isBlank(token)) { 21 token = UUIDGenerator.getUUID(); 22 tokenMap.set(token); 23 } 24 boolean flag = false; 25 Jedis client = null; 26 try { 27 client = jedisPool.getResource(); 28 String ret = client.set(lock, token, "NX", "EX", seconds); 29 if (ret == null) {// 该lock的锁已经存在 30 // String origToken = client.get(lock);// 即使lock已经过期也可以 31 // if (token.equals(origToken)||origToken==null) {// token相同默认为同一线程,所以token应该尽量长且随机,保证不同线程的该值不相同 32 // ret = client.set(lock, token, "NX", "EX", seconds);// 33 // if ("OK".equalsIgnoreCase(ret)) 34 // flag = true; 35 // }
ret=client.cas(lock,origToken,token,seconds);
if("OK".equalsIgnoreCase(ret)){
flag=true;
} 36 } else if ("OK".equalsIgnoreCase(ret)) 37 flag = true; 38 } catch (Exception e) { 39 logger.error(" lock{} 失败"); 40 throw new RedisException(e); 41 } finally { 42 if (client != null) 43 client.close(); 44 } 45 return flag; 46 } 47 48 /** 49 * redis可以保证lua中的键的原子操作 unlock:lock调用完之后需unlock,否则需等待lock自动过期 50 * 51 * @param lock 52 * token 53 * 只有线程已经获取了该锁才能释放它(token相同表示已获取) 54 */ 55 public void unlock(final String lock) { 56 Jedis client = null; 57 final String token = tokenMap.get(); 58 if (StringUtil.isBlank(token)) 59 return; 60 try { 61 client = jedisPool.getResource(); 62 final String script = "if redis.call(\"get\",\"" + lock + "\") == \"" + token + "\"then return redis.call(\"del\",\"" + lock + "\") else return 0 end "; 63 client.eval(script); 64 } catch (Exception e) { 65 logger.error(" unlock{} 失败"); 66 throw new RedisException(e); 67 } finally { 68 if (client != null) 69 client.close(); 70 } 71 72 } 73 74 public static void main(String[] args) { 75 ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); 76 final RedisUtil redis = ctx.getBean(RedisUtil.class); 77 for (int i = 0; i < 100; i++) { 78 new Thread(new Runnable() { 79 String key = "cheng"; 80 81 @Override 82 public void run() { 83 boolean lock = redis.lock(key, 30); 84 System.out.print(lock + "-"); 85 86 } 87 }).start(); 88 ; 89 } 90 // redis.unlock(key); 91 // ctx.close(); 92 }
运行main方法:
结果:true-false-......false-