程序在單臺應用的時候,程序上完全可以用synchronized同步鎖來限制多線程對共享資源的訪問,但在分布式環境下同步鎖無法控制不同進程之間的線程,這種情況下就需要找一種單進程可串行處理的“鎖”,
所以產生了分布式鎖這個玩意;
至于 分布式鎖我了解到的實現有三種方案
期初在做這個的時候也考慮了他們的實現優缺點
zookeeper分布式鎖
優點:
鎖安全性高,zk可持久化。當然 redis 也可以實現持久化。
缺點:
性能開銷比較高。因為其需要動態產生、銷毀瞬時節點來實現鎖功能。
memcached分布式鎖
優點:
并發高效。
缺點:
(1)memcached采用列入LRU置換策略,所以如果內存不夠,可能導致緩存中的鎖信息丟失。
(2)memcached無法持久化,一旦重啟,將導致信息丟失。
redis 分布式鎖
也是我最推薦的
redis分布式鎖即可以結合zk分布式鎖鎖高度安全和memcached并發場景下效率很好的優點,可以利用jedis客戶端實現。代碼見最后。
怎么使用
RedisLock redisLockN = redisLock.newInstance(RedisKey.KEY_SETUP_ACCOUNT.getCode(), 10000);
if (redisLockN.lock()) {
//業務代碼
redisLockN.unlock();
LOGGER.info(" 釋放redis分布式鎖");
} else {
LOGGER.error("獲取redis分布式鎖失敗 請檢查");
throw new 拋出異常
}
具體實現
把我們的核心代碼都復制出來,不知道老大會不會出來打我
@Component
public class RedisLock {
private static final Logger LOGGER = LoggerFactory.getLogger(RedisLock.class);
private static final int DEFAULT_LOCK_EXPIRSE_MILL_SECONDS = 1000*30;
private static final int DEFAULT_LOCK_WAIT_DEFAULT_TIME_OUT = 1000*10;
private static final int DEFAULT_LOOP_WAIT_TIME = 150;
private boolean lock = false;
private String lockKey = null;
private int lockExpriseTimeout;
private int lockWaitTimeOut;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Deprecated
public RedisLock(){}
public RedisLock(String lockKey,int lockExpriseTimeout,int lockWaitTimeOut)
{
this.lockKey = lockKey;
this.lockExpriseTimeout = lockExpriseTimeout;
this.lockWaitTimeOut = lockWaitTimeOut;
}
public RedisLock newInstance(String lockKey,int lockWaitTimeOut)
{
if(lockWaitTimeOut == 0)
{
lockWaitTimeOut = DEFAULT_LOCK_WAIT_DEFAULT_TIME_OUT;
}
RedisLock lock = new RedisLock(lockKey,DEFAULT_LOCK_EXPIRSE_MILL_SECONDS,lockWaitTimeOut);
lock.setStringRedisTemplate(this.stringRedisTemplate);
return lock;
}
private boolean putIfAbsent(String expirseTimeStr)
{
return this.stringRedisTemplate.opsForValue().setIfAbsent(this.lockKey,expirseTimeStr);
}
private String getAndSet(String expirseTimeStr)
{
return this.stringRedisTemplate.opsForValue().getAndSet(this.lockKey,expirseTimeStr);
}
public boolean lock()
{
int lockWaitMillSeconds = this.lockWaitTimeOut;
String redis_value = String.valueOf(System.currentTimeMillis()+this.lockExpriseTimeout);
while(lockWaitMillSeconds > 0)
{
lock = this.putIfAbsent(redis_value);
if(lock)
{
this.lock = true;
return this.lock;
}
else
{
String oldRedisValue = this.stringRedisTemplate.opsForValue().get(this.lockKey);
long currentTimeMillSeconds = System.currentTimeMillis();
if(!StringUtils.isEmpty(oldRedisValue) && Long.parseLong(oldRedisValue) < currentTimeMillSeconds)
{
String currentRedisValue = this.getAndSet(String.valueOf(currentTimeMillSeconds+this.lockExpriseTimeout));
if(oldRedisValue.equals(currentRedisValue))
{
this.lock = true;
return this.lock;
}
}
}
lockWaitMillSeconds -= DEFAULT_LOOP_WAIT_TIME;
try
{
Thread.sleep(DEFAULT_LOOP_WAIT_TIME);
}
catch (Exception ex)
{
LOGGER.error("redis同步鎖 出現未知異常",ex);
}
}
return false;
}
public void unlock()
{
if (this.lock) {
this.stringRedisTemplate.delete(this.lockKey);
this.lock = false;
}
}
public boolean isLock() {
return lock;
}
public void setLock(boolean lock) {
this.lock = lock;
}
public String getLockKey() {
return lockKey;
}
public void setLockKey(String lockKey) {
this.lockKey = lockKey;
}
public int getLockExpriseTimeout() {
return lockExpriseTimeout;
}
public void setLockExpriseTimeout(int lockExpriseTimeout) {
this.lockExpriseTimeout = lockExpriseTimeout;
}
public int getLockWaitTimeOut() {
return lockWaitTimeOut;
}
public void setLockWaitTimeOut(int lockWaitTimeOut) {
this.lockWaitTimeOut = lockWaitTimeOut;
}
public void setStringRedisTemplate(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
}