分佈式鎖通常有三種實現方式:1. 數據庫樂觀鎖;2. 基於Redis的分佈式鎖;3. 基於ZooKeeper的分佈式鎖。本篇博客將介紹第二種方式,基於Redis實現分佈式鎖。html
首先,爲了確保分佈式鎖可用,咱們至少要確保鎖的實現同時知足如下四個條件:java
上面摘自博客Redis分佈式鎖的正確實現方式,下面我將經過Spring Boot的RedisTemplate實現分佈式鎖,不足之處請指出!redis
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.stereotype.Component; import java.util.Arrays; import java.util.concurrent.TimeUnit; @Component public class RedisLock { @Autowired private StringRedisTemplate template; @Autowired private DefaultRedisScript<Long> redisScript; private static final Long RELEASE_SUCCESS = 1L; private long timeout = 3000; public boolean lock(String key, String value) { long start = System.currentTimeMillis(); while (true) { //檢測是否超時 if (System.currentTimeMillis() - start > timeout) { return false; } //執行set命令 Boolean absent = template.opsForValue().setIfAbsent(key, value, timeout, TimeUnit.MILLISECONDS);//1 //其實不必判NULL,這裏是爲了程序的嚴謹而加的邏輯 if (absent == null) { return false; } //是否成功獲取鎖 if (absent) { return true; } } } public boolean unlock(String key, String value) { //使用Lua腳本:先判斷是不是本身設置的鎖,再執行刪除 Long result = template.execute(redisScript, Arrays.asList(key,value)); //返回最終結果 return RELEASE_SUCCESS.equals(result); } public void setTimeout(long timeout) { this.timeout = timeout; } @Bean public DefaultRedisScript<Long> defaultRedisScript() { DefaultRedisScript<Long> defaultRedisScript = new DefaultRedisScript<>(); defaultRedisScript.setResultType(Long.class); defaultRedisScript.setScriptText("if redis.call('get', KEYS[1]) == KEYS[2] then return redis.call('del', KEYS[1]) else return 0 end"); // defaultRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("delete.lua"))); return defaultRedisScript; } }