程序在单台应用的时候,程序上完全可以用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;
}
}