分佈式鎖,其實原理是就是多臺機器,去爭搶一個資源,誰爭搶成功,那麼誰就持有了這把鎖,而後去執行後續的業務邏輯,執行完畢後,把鎖釋放掉。java
能夠經過多種途徑實現分佈式鎖,例如利用數據庫(mysql等),插入一條記錄(惟一索引),誰插入成功,誰就持有鎖;還可經過zookeeper來實現分佈式鎖,誰建立節點成功,誰就持有鎖。本文介紹經過redis來實現分佈式鎖。mysql
本文使用springboot提供的RedisTemplate來操做redis,能夠參考我以前的文章【快學springboot】13.操做redis之String數據結構,這裏對使用RedisTemplate來操做redis作了介紹。固然也能夠直接使用jedis來操做redis,你們能夠參考下jedis的文檔,使用上都是大同小異的。redis
第一步:經過redis的setnx方式(不存在則設置),往redis上設置一個帶有過時時間的key,若是設置成功,則得到了分佈式鎖。這裏設置過時時間,是防止在釋放鎖的時候出現異常致使鎖釋放不掉。spring
第二步:執行完業務操做以後,刪除該鎖。sql
新建一個DistributedLock.class,注入StringRedisTemplate。數據庫
@Component public class DistributedLock { @Autowired private StringRedisTemplate redisTemplate; }
得到鎖springboot
/** * 得到鎖 */ public boolean getLock(String lockId, long millisecond) { Boolean success = redisTemplate.opsForValue().setIfAbsent(lockId, "lock", millisecond, TimeUnit.MILLISECONDS); return success != null && success; }
setIfAbsent方法,就是當鍵不存在的時候,設置,而且該方法能夠設置鍵的過時時間。該方法對應到redis的原生命令就是:數據結構
SET lockId content PX millisecond NX
至於設置多少的過時時間合適,這個是沒有定論的,須要根據真是的業務場景來衡量。app
釋放鎖分佈式
當處理完業務邏輯後,須要手動的把鎖釋放掉。
public void releaseLock(String lockId) { redisTemplate.delete(lockId); }
釋放鎖的操做比較簡單,直接刪除以前設置的鍵便可。其實,基於redis實現分佈式鎖的方式,在釋放鎖的時候,是存在釋放失敗的風險的(好比網路抖動什麼的),這也是爲何在設置鎖的時候須要設置過時時間的緣由,能夠防止在出現異常的時候,鎖會自動的消失掉。同時,咱們也能夠增長几回失敗以後的重試機制。
新建一個BusinessTask.java,代碼以下:
@Component public class BusinessTask { private final static String LOCK_ID = "happyjava"; @Autowired DistributedLock distributedLock; @Scheduled(cron = "0/10 * * * * ? ") public void doSomething() { boolean lock = distributedLock.getLock(LOCK_ID, 10 * 1000); if (lock) { System.out.println("執行任務"); distributedLock.releaseLock(LOCK_ID); } else { System.out.println("沒有搶到鎖"); } } }
這裏使用了springboot的Scheduled註解來實現定時任務,該cron表達式的意思是每10秒鐘,執行一次任務,而後咱們啓動兩次該項目,觀察一段時間執行結果:
第一個springboot任務:
第二個springboot任務:
兩個任務在交替的執行任務,證實了同一時刻只有一個應用持有了鎖。
本文主要介紹瞭如何使用Java代碼(springboot的restTemplate)實現Redis分佈式鎖,對於加鎖和解鎖也分別給出了示例代碼。其實咱們還能夠嘗試使用Redisson實現分佈式鎖,這是Redis官方提供的Java組件,這個後續再介紹吧。