在分布式系統中,經常會出現需要競爭同一資源的情況,使用redis可以實現分布式鎖。
前提:redis集群已經整合項目,並且可以直接注入JedisCluster使用:
@Autowired private JedisCluster jedisCluster;
1. 新建RedisLockManger分布式鎖管理器,並且如上注入 JedisCluster :
package com.jarfk.util.redis; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import redis.clients.jedis.JedisCluster; import javax.annotation.PostConstruct; import java.util.concurrent.TimeUnit; /** * redis集群分布式鎖管理器,支持對單個資源加鎖解鎖,或給一批資源的批量加鎖及解鎖 * Created by Administrator on 2017/10/12 0012. */ @Component public class RedisLockManger { private static final Logger LOGGER = LoggerFactory.getLogger(RedisLockManger.class); //設置3秒過期 private static final int DEFAULT_SINGLE_EXPIRE_TIME = 3; // private static final int DEFAULT_BATCH_EXPIRE_TIME = 6; //static的變量無法注解 @Autowired private JedisCluster jc; private static RedisLockManger lockManger; public RedisLockManger() { } @PostConstruct private void init() { lockManger = this; lockManger.jc = this.jc; } /** * 獲取鎖 如果鎖可用 立即返回true, 否則立即返回false,作為非阻塞式鎖使用 * @param key * @return */ public boolean tryLock(String key/* , String value*/) { try { return tryLock(key, key, 0L, null); } catch (InterruptedException e) { e.printStackTrace(); } return false; } /** * 鎖在給定的等待時間內空閑,則獲取鎖成功 返回true, 否則返回false,作為阻塞式鎖使用 * @param key 鎖鍵 * @param value 被誰鎖定 * @param timeout 嘗試獲取鎖時長,建議傳遞500,結合實踐單位,則可表示500毫秒 * @param unit,建議傳遞TimeUnit.MILLISECONDS * @return * @throws InterruptedException */ public boolean tryLock(String key , String value , long timeout , TimeUnit unit) throws InterruptedException { //納秒 long begin = System.nanoTime(); do { //LOGGER.debug("{}嘗試獲得{}的鎖.", value, key); Long i = lockManger.jc.setnx(key, value); if (i == 1) { lockManger.jc.expire(key, DEFAULT_SINGLE_EXPIRE_TIME); LOGGER.debug(value + "-成功獲取{}的鎖,設置鎖過期時間為{}秒 ", key, DEFAULT_SINGLE_EXPIRE_TIME); return true; } else { // 存在鎖 ,但可能獲取不到,原因是獲取的一剎那間 // String desc = lockManger.jc.get(key); // LOGGER.error("{}正被{}鎖定.", key, desc); } if (timeout == 0) { break; } //在其睡眠的期間,鎖可能被解,也可能又被他人占用,但會嘗試繼續獲取鎖直到指定的時間 Thread.sleep(100); } while ((System.nanoTime() - begin) < unit.toNanos(timeout)); //因超時沒有獲得鎖 return false; } /** * 釋放單個鎖 * @param key 鎖鍵 */ public void unLock(String key/*, String value*/) { lockManger.jc.del(key); LOGGER.debug("{}鎖被{}釋放 .", key, key); } }
2. 使用示例:
首先在需要加鎖的地方注入分布式鎖管理器:
@Autowired private RedisLockManger redisLock;
然后調用即可,如:
if (redisLock.tryLock("statusCheck")) { //此處代碼是鎖上的 logger.debug("-----------------------:10秒執行一次!每次只有一個程序運行"); //釋放鎖,正常情況下,此處代碼要注釋掉,以免鎖被釋放,需要釋放時可以根據自己邏輯的需要 //redisLock.unLock("statusCheck"); }
首先注入需要的