<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
為了保證一個方法在高並行情況下的同一時間只能被同一個執行緒執行,在傳統單體應用單機部署的情況下,可以使用Java並行處理相關的API(如ReentrantLcok或synchronized)進行互斥控制。但是,隨著業務發展的需要,原單體單機部署的系統被演化成分散式系統後,由於分散式系統多執行緒、多程序並且分佈在不同機器上,這將使原單機部署情況下的並行控制鎖策略失效,為了解決這個問題就需要一種跨JVM的互斥機制來控制共用資源的存取,這就是分散式鎖要解決的問題。
1、處理效率提升:應用分散式鎖,可以減少重複任務的執行,避免資源處理效率的浪費;
2、資料準確性保障:使用分散式鎖可以放在資料資源的並行存取,避免資料不一致情況,甚至資料損失等。
例如:
分散式任務排程平臺保證任務的冪等性。
分散式全域性id的生成
思路:Redis實現分散式鎖基於SetNx命令,因為在Redis中key是保證是唯一的。所以當多個執行緒同時的建立setNx時,只要誰能夠建立成功誰就能夠獲取到鎖。
Set 命令: 每次 set 時,可以修改原來舊值;
SetNx命令:每次SetNx檢查該 key是否已經存在,如果已經存在的話不會執行任何操作。返回為0 如果已經不存在的話直接新增該key。
1:新增key成功, 0:失敗
獲取鎖的時候:當多個執行緒同時建立SetNx k,只要誰能夠建立成功誰就能夠獲取到鎖。
釋放鎖:可以對該key設定一個有效期可以避免死鎖的現象。
1、增加maven依賴
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency>
2、編寫Jedis連線Redis工具類
public class RedisClientUtil { //protected static Logger logger = Logger.getLogger(RedisUtil.class); private static String IP = "www.kaicostudy.com"; //Redis的埠號 private static int PORT = 6379; //可用連線範例的最大數目,預設值為8; //如果賦值為-1,則表示不限制;如果pool已經分配了maxActive個jedis範例,則此時pool的狀態為exhausted(耗盡)。 private static int MAX_ACTIVE = 100; //控制一個pool最多有多少個狀態為idle(空閒的)的jedis範例,預設值也是8。 private static int MAX_IDLE = 20; //等待可用連線的最大時間,單位毫秒,預設值為-1,表示永不超時。如果超過等待時間,則直接丟擲JedisConnectionException; private static int MAX_WAIT = 3000; private static int TIMEOUT = 3000; //在borrow一個jedis範例時,是否提前進行validate操作;如果為true,則得到的jedis範例均是可用的; private static boolean TEST_ON_BORROW = true; //在return給pool時,是否提前進行validate操作; private static boolean TEST_ON_RETURN = true; private static JedisPool jedisPool = null; /** * redis過期時間,以秒為單位 */ public final static int EXRP_HOUR = 60 * 60; //一小時 public final static int EXRP_DAY = 60 * 60 * 24; //一天 public final static int EXRP_MONTH = 60 * 60 * 24 * 30; //一個月 /** * 初始化Redis連線池 */ private static void initialPool() { try { JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(MAX_ACTIVE); config.setMaxIdle(MAX_IDLE); config.setMaxWaitMillis(MAX_WAIT); config.setTestOnBorrow(TEST_ON_BORROW); jedisPool = new JedisPool(config, IP, PORT, TIMEOUT, "123456"); } catch (Exception e) { //logger.error("First create JedisPool error : "+e); e.getMessage(); } } /** * 在多執行緒環境同步初始化 */ private static synchronized void poolInit() { if (jedisPool == null) { initialPool(); } } /** * 同步獲取Jedis範例 * * @return Jedis */ public synchronized static Jedis getJedis() { if (jedisPool == null) { poolInit(); } Jedis jedis = null; try { if (jedisPool != null) { jedis = jedisPool.getResource(); } } catch (Exception e) { e.getMessage(); // logger.error("Get jedis error : "+e); } return jedis; } /** * 釋放jedis資源 * * @param jedis */ public static void returnResource(final Jedis jedis) { if (jedis != null && jedisPool != null) { jedisPool.returnResource(jedis); } } public static Long sadd(String key, String... members) { Jedis jedis = null; Long res = null; try { jedis = getJedis(); res = jedis.sadd(key, members); } catch (Exception e) { //logger.error("sadd error : "+e); e.getMessage(); } return res; } }
3、編寫Redis鎖的工具類
public class RedisLock { private static final int setnxSuccss = 1; /** * 獲取鎖 * * @param lockKey 定義鎖的key * @param notLockTimeOut 沒有獲取鎖的超時時間 * @param lockTimeOut 使用鎖的超時時間 * @return */ public String getLock(String lockKey, int notLockTimeOut, int lockTimeOut) { // 獲取Redis連線 Jedis jedis = RedisClientUtil.getJedis(); // 定義沒有獲取鎖的超時時間 Long endTimeOut = System.currentTimeMillis() + notLockTimeOut; while (System.currentTimeMillis() < endTimeOut) { String lockValue = UUID.randomUUID().toString(); // 如果在多執行緒情況下誰能夠setnx 成功返回0 誰就獲取到鎖 if (jedis.setnx(lockKey, lockValue) == setnxSuccss) { jedis.expire(lockKey, lockTimeOut / 1000); return lockValue; } // 否則情況下 在超時時間內繼續迴圈 } try { if (jedis != null) { jedis.close(); } } catch (Exception e) { e.printStackTrace(); } return null; } /** * 釋放鎖 其實就是將該key刪除 * * @return */ public Boolean unLock(String lockKey, String lockValue) { Jedis jedis = RedisClientUtil.getJedis(); // 確定是對應的鎖 ,才刪除 if (lockValue.equals(jedis.get(lockKey))) { return jedis.del(lockKey) > 0 ? true : false; } return false; } }
4、測試方法
private RedisLock redisLock = new RedisLock(); private String lockKey = "kaico_lock"; /** * 測試Jedis實現分散式鎖 * @return */ @GetMapping("/restLock1") public String restLock1(){ // 1.獲取鎖 String lockValue = redisLock.getLock(lockKey, 5000, 5000); if (StringUtils.isEmpty(lockValue)) { System.out.println(Thread.currentThread().getName() + ",獲取鎖失敗!"); return "獲取鎖失敗"; } // 2.獲取鎖成功執行業務邏輯 System.out.println(Thread.currentThread().getName() + ",獲取成功,lockValue:" + lockValue); // 3.釋放lock鎖 redisLock.unLock(lockKey, lockValue); return ""; }
依賴於之前的專案
1、編寫鎖的工具類方法
@Component public class SpringbootRedisLockUtil { @Autowired public RedisTemplate redisTemplate; // 解鎖原子性操作指令碼 public static final String unlockScript="if redis.call("get",KEYS[1]) == ARGV[1]n" + "thenn" + " return redis.call("del",KEYS[1])n" + "elsen" + " return 0n" + "end"; /** * 加鎖,有阻塞 * @param name * @param expire * @param timeout * @return */ public String lock(String name, long expire, long timeout) throws UnsupportedEncodingException { long startTime=System.currentTimeMillis(); String token; do{ token=tryLock(name,expire); if(token==null){ //設定等待時間,若等待時間過長則獲取鎖失敗 if((System.currentTimeMillis()-startTime)>(timeout-50)){ break; } try { Thread.sleep(50);//try it again per 50 } catch (InterruptedException e) { e.printStackTrace(); } } }while (token==null); return token; } /** * 解鎖 * @param name * @param token * @return */ public Boolean unlock(String name, String token) throws UnsupportedEncodingException { byte[][] keyArgs=new byte[2][]; keyArgs[0]= name.getBytes(Charset.forName("UTF-8")); keyArgs[1]= token.getBytes(Charset.forName("UTF-8")); RedisConnectionFactory connectionFactory = redisTemplate.getConnectionFactory(); RedisConnection connection = connectionFactory.getConnection(); try{ Long result = connection.scriptingCommands().eval(unlockScript.getBytes(Charset.forName("UTF-8")), ReturnType.INTEGER, 1, keyArgs); if(result!=null&&result>0){ return true; } }finally { RedisConnectionUtils.releaseConnection(connection,connectionFactory); } return false; } /** * 加鎖,無阻塞 * @param name * @param expire * @return */ public String tryLock(String name, long expire) throws UnsupportedEncodingException { String token= UUID.randomUUID().toString(); RedisConnectionFactory connectionFactory = redisTemplate.getConnectionFactory(); RedisConnection connection = connectionFactory.getConnection(); try{ Boolean result = connection.set(name.getBytes(Charset.forName("UTF-8")), token.getBytes(Charset.forName("UTF-8")), Expiration.from(expire, TimeUnit.MILLISECONDS), RedisStringCommands.SetOption.SET_IF_ABSENT); if(result!=null&&result){ return token; } } finally { RedisConnectionUtils.releaseConnection(connection,connectionFactory); } return null; } }
2、測試類
@Autowired private SpringbootRedisLockUtil springbootRedisLockUtil; @PostMapping("/restLock1") public void restLock2() throws UnsupportedEncodingException { String token; token=springbootRedisLockUtil.lock(Thread.currentThread().getName(),1000,11000); if(token!=null){ System.out.println("我拿到鎖了哦!"); } else{ System.out.println("我沒有拿到鎖!"); } springbootRedisLockUtil.unlock(Thread.currentThread().getName(),token); }
到此這篇關於Redis超詳細分析分散式鎖的文章就介紹到這了,更多相關Redis分散式鎖內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45