利用redis能夠實現分佈式鎖,demo以下:redis
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方法:app
結果:true-false-......false-分佈式