基于redis的分布式锁实现

最近项目中用到了分布式锁,研究了下redis相关的方法,顺手实现了一下。

接口类:

import java.util.concurrent.TimeUnit;

public interface RedisLock {

	/**
	 * 尝试获取key上的锁,获取不到立即返回false
	 * @param key
	 * @return
	 */
	public boolean tryLock(String key);

	/**
	 * 尝试获取key上的锁(指定锁的超时时间),获取不到立即返回false
	 * @param key
	 * @param expireTimeMillis 锁的超时时间,毫秒
	 * @return
	 */
	public boolean tryLock(String key,long expireTimeMillis);
	
	/**
	 * 循环尝试获取key上的锁(指定循环尝试超时时间)
	 * @param key
	 * @param timeout 循环尝试超时时间
	 * @param unit 时间单位
	 * @return
	 * @throws InterruptedException
	 */
	public boolean tryLock(String key,long timeout, TimeUnit unit) throws InterruptedException;

	/**
	 * 循环尝试获取key上的锁(指定循环尝试超时时间,锁的超时时间)
	 * @param key
	 * @param time 循环尝试超时时间
	 * @param unit 时间单位
	 * @param expireTimeMillis 锁的超时时间,毫秒
	 * @return
	 * @throws InterruptedException
	 */
	public boolean tryLock(String key,long time, TimeUnit unit,long expireTimeMillis) throws InterruptedException;
	
	/**
	 * 释放key上的锁
	 * @param key
	 */
	public void unLock(String key);
}

  方法定义上借鉴了java.util.concurrent.locks中Lock类,具体方法作用见代码注释。

实现类:

import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.StringUtils;


public class RedisLockImpl implements RedisLock{
	
	//这里引用自己项目中的redis客户端
	private RedisClient redisClient;
	
	/**
	 * 默认循环获取锁sleep时间1s
	 */
	private static final long SLEEP_MILLIS = 1000L;
	
	/**
	 * 默认锁超时时间10s
	 */
	private static final long DEFAULT_EXPIRE_TIMEMILLIS = 10000L;
	
	
	public boolean _tryLock(String key,long expireTimeMillis) {
		if(StringUtils.isEmpty(key)){
			throw new NullPointerException("lock key can not be empty");
		}
		boolean result = redisClient.setnx(key, String.valueOf(System.currentTimeMillis())) == 1L;
		if(!result){
			Long currentTime = System.currentTimeMillis();
			String t1 = redisClient.get(key);
			if(StringUtils.isEmpty(t1)){
				return false;
			}
			//超时
			if(currentTime - Long.parseLong(t1) >= expireTimeMillis){
				String t2 = redisClient.getSet(key, String.valueOf(currentTime));
				if(StringUtils.isNotEmpty(t2) && t2.equals(t1)){
					result = true;
				}
			}
		}
		return result;
	}
	
	public boolean tryLock(String key){
		return _tryLock(key,DEFAULT_EXPIRE_TIMEMILLIS);
	}
	
	@Override
	public boolean tryLock(String key,long expireTimeMillis){
		return _tryLock(key,expireTimeMillis);
	}

	@Override
	public boolean tryLock(String key, long time, TimeUnit unit) throws InterruptedException{
		return _tryLock(key, time, unit, DEFAULT_EXPIRE_TIMEMILLIS);
	}
	
	@Override
	public boolean tryLock(String key,long time, TimeUnit unit,long expireTimeMillis) throws InterruptedException{
		return _tryLock(key,time, unit,expireTimeMillis);
	}
	
	private boolean _tryLock(String key, long time, TimeUnit unit, long expireTimeMillis) throws InterruptedException{
		if(StringUtils.isEmpty(key)){
			throw new NullPointerException("lock key can not be empty");
		}
		return _tryLockNanos(key, unit.toNanos(time),expireTimeMillis);
	}
	
	private boolean _tryLockNanos(String key,long nanos,long expireTimeMillis) throws InterruptedException{
		return _tryLock(key,expireTimeMillis) || _doTryLockNanos(key,nanos,expireTimeMillis);
	}
	
	private boolean _doTryLockNanos(String key,long nanosTimeout,long expireTimeMillis) throws InterruptedException{
        if(nanosTimeout <= 0L){
        	return false;
        }
        final long deadline = System.nanoTime() + nanosTimeout;
        for(;;){
    		if(_tryLock(key,expireTimeMillis)){
    			return true;
    		}
    		nanosTimeout = deadline - System.nanoTime();
    		if(nanosTimeout <= 0L){
    			return false;
    		}
    		
    		Thread.sleep(SLEEP_MILLIS);
    		
    		if (Thread.interrupted()){
    			throw new InterruptedException();
    		}
    	}
	}
	
	@Override
	public void unLock(String key) {
		redisClient.del(key);
	}

}

  具体使用示例:

if(redisLock.tryLock("lockKey")){
    try{
        //业务逻辑
    }finally{
        redisLock.unLock("lockKey");
    }
}

  

posted @ 2016-08-11 18:08  小小几分  阅读(123)  评论(0)    收藏  举报