平常的開發中,咱們常常會遇到不一樣線程或進程須要互斥訪問資源的狀況,這時候分佈式的鎖就是很是有必要的了。分佈式鎖實現方式能夠有不少種:如mysql、redis、zookeeper等。java
這裏簡單地分享一下基於redis的實現。mysql
這裏使用了幾個主要的redis命令: set 、get 、del。redis
SET的命令格式:sql
SET key value NX PX milliseconds SET key value NX EX seconds
這裏必定要使用NX,由於NX表示只有key不存在時,SET才能成功。dom
從接口上,鎖應該分爲獲取鎖和釋放鎖。分佈式
獲取鎖:經過set命令設置一個key的值,並返回鎖的內容。若是沒有set成功,即表示已經被其它線程或進程佔用,能夠繼續嘗試獲取。ui
釋放鎖:經過del命令將這個key值刪除便可,可是爲保證釋放鎖的可靠性須要驗證鎖的內容。this
下面貼上具體的實現代碼,註釋也比較清楚,再也不詳細描述。線程
package com.zheng.coderepo.redis; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import java.util.UUID; import java.util.concurrent.TimeUnit; /** * redis分佈式鎖 * Created by zhangchaozheng on 16-7-28. */ public class RedisLock { private final static String UNLOCK_LUA = "" + "if redis.call(\"get\",KEYS[1]) == ARGV[1] " + "then " + " return redis.call(\"del\",KEYS[1]) " + "else " + " return 0 " + "end"; //等待鎖時間 private final static long WAIT_LOCK_TIME = 300; private JedisPool jedisPool; public RedisLock(JedisPool jedisPool) { this.jedisPool = jedisPool; } /** * 若是分佈式鎖不可用, 當前線程將繼續請求, 直到鎖可用. * * @param lockKey 鎖的鍵值 * @param timeout 超時時間.單位:毫秒. * @return 鎖的值. */ public String lock(String lockKey, long timeout) { String lockVal = UUID.randomUUID().toString(); for (; ; ) { if (!tryLock(lockKey, lockVal, timeout)) { try { TimeUnit.MILLISECONDS.sleep(WAIT_LOCK_TIME); } catch (InterruptedException e) { } } else { break; } } return lockVal; } /** * 嘗試得到分佈式鎖, 若是鎖可用, 返回true * * @param lockKey 鎖的鍵值 * @param value 鎖的值,推薦使用隨機字符. * @param timeout 超時時間.單位:毫秒. * @return true/false */ public boolean tryLock(String lockKey, String value, long timeout) { Jedis jedis = jedisPool.getResource(); try { Object result = jedis.set(lockKey, value, "NX", "PX", timeout); return "OK".equalsIgnoreCase(result + ""); } finally { quietlyCloseJedis(jedis); } } /** * 釋放鎖. * * @param lockKey 鎖的鍵值 * @param value 鎖的值.必需要和上鎖的值一致. */ public void unlock(String lockKey, String value) { Jedis jedis = jedisPool.getResource(); try { jedis.eval(UNLOCK_LUA, 1, lockKey, value); } finally { quietlyCloseJedis(jedis); } } /** * 關閉redis鏈接。jedis 3.0開始,官方拋棄了原來的returnResource和returnBrokenResource。 * * @param jedis */ private void quietlyCloseJedis(Jedis jedis) { if (jedis != null) { jedis.close(); } } }