Redis分布式锁实现类

  Redis分布式锁实现类,RedisDistributedLock。

package com.dami.util.redis;
import java.util.Collections;
import java.util.UUID;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;

import redis.clients.jedis.Jedis;
 
/**
 * Redis分布式锁实现类
 * @ClassName: RedisDistributedLock
 * @Description: TODO(这里用一句话描述这个类的作用)
 * @author Administrator
 * @date 2018-11-22 下午2:11:07
 */
public class RedisDistributedLock{
	
	private final Logger logger = LoggerFactory.getLogger(RedisDistributedLock.class);
	// 锁标志
	public static final String PREFIX = "lock_";
	
	// SET_IF_NOT_EXIST 标志,表示如果当key不存在时进行set操作,否则不处理
	private static final String NX = "NX";
	// 过期时间标志, EX 表示以秒作为单位
	private static final String EX = "EX";
	// 锁成功标志
	private static final String LOCK_SUCCESS = "OK";
	// 解锁成功标志
	private static final long DEL_SUCCESS = 1L;
	// 默认过期时间
	private static final int DEFAULT_EXPIRE_TIME = 180;
	// 默认重试次数
	private static final int DEFAULT_RETRY_TIME = 3;
	// 默认重试暂停时间
	private static final int DEFAULT_INTERNAL_TIME = 100;
	// 解锁时的 lua语言Script
	private static final String DEL_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
	// 过期时间(秒)
	private int expireSeconds;
	// 锁重试次数
	private int retryTimes;
	// 锁重试间隔
	private int internalTime;
	// 加锁者标志, 解锁时只有与加锁时的值一致才允许解锁
	private final String lockId;
	@Autowired
	private static RedisTemplate<String, Object> redisTemplate;

	public RedisDistributedLock() {
		this(redisTemplate, DEFAULT_EXPIRE_TIME, DEFAULT_RETRY_TIME, DEFAULT_INTERNAL_TIME);
	}
	public RedisDistributedLock(RedisTemplate<String, Object> redisTemplate) {
		this(redisTemplate, DEFAULT_EXPIRE_TIME, DEFAULT_RETRY_TIME, DEFAULT_INTERNAL_TIME);
	}

	public RedisDistributedLock(RedisTemplate<String, Object> redisTemplate, int expireSeconds, int retryTimes, int internalTime) {
		this.redisTemplate = redisTemplate;
		this.expireSeconds = expireSeconds;
		this.retryTimes = retryTimes;
		this.internalTime = internalTime;
		this.lockId = UUID.randomUUID().toString();
	}
	/**
	 * 获取redis 锁
	 * @Title: lock
	 * @Description: TODO(这里用一句话描述这个方法的作用)
	 * @author Administrator
	 * @date 2018-11-22 下午1:55:57
	 * @param key
	 * @return
	 */
	public boolean lock(String key, final String lockId) {
		if (StringUtils.isBlank(key)) {
			return false;
		}
	
		final String fullKey = PREFIX + key;
		//logger.debug("尝试获取锁, 锁Key:{}, 锁标识:{}", fullKey, lockId);
		System.out.println("尝试获取锁, 锁Key:{"+fullKey+"}, 锁标识:{"+lockId+"}");
		for (int i = 0; i < retryTimes; i++) {
			//logger.debug("第 {} 次尝试获取锁", i + 1);
			System.out.println("第 {"+(i + 1)+"} 次尝试获取锁");
			
			String status = redisTemplate.execute(new RedisCallback<String>() {
				@Override
				public String doInRedis(RedisConnection connection) throws DataAccessException {
					try {
						Jedis jedis = (Jedis) connection.getNativeConnection();
						return jedis.set(fullKey, lockId, NX, EX, expireSeconds);
					} catch (Exception e) {
						return "ERROR";
					}
				}
			});
			if (LOCK_SUCCESS.equalsIgnoreCase(status)) {
				//logger.debug("获取锁成功, 锁Key:{}, 锁标识:{}", fullKey, lockId);
				System.out.println("获取锁成功, 锁Key:{"+fullKey+"}, 锁标识:{"+lockId+"}");
				return true;
			}
			try {
				Thread.sleep(internalTime);
			} catch (InterruptedException e) {
				break;
			}
		}
		//logger.debug("尝试获取锁 {} 次后失败, 锁Key:{}", retryTimes, fullKey);
		System.out.println("尝试获取锁 {"+retryTimes+"} 次后失败, 锁Key:{"+fullKey+"}" );
		return false;
	}
	/**
	 * 释放redis锁
	 * @Title: unlock
	 * @Description: TODO(这里用一句话描述这个方法的作用)
	 * @author Administrator
	 * @date 2018-11-22 下午1:55:34
	 * @param key
	 * @return
	 */
	public boolean unlock(String key, final String lockId) {
		if (StringUtils.isBlank(key)) {
			return false;
		}
		final String fullKey = PREFIX + key;
		//logger.debug("尝试解锁, 锁Key:{}, 锁标识:{}", fullKey, lockId);
		System.out.println("尝试解锁, 锁Key:{"+fullKey+"}, 锁标识:{"+lockId+"}");
		Object value = redisTemplate.execute(new RedisCallback<Object>() {
			@Override
			public Object doInRedis(RedisConnection connection) throws DataAccessException {
				try {
					Jedis jedis = (Jedis) connection.getNativeConnection();
					return jedis.eval(DEL_SCRIPT, Collections.singletonList(fullKey), Collections.singletonList(lockId));
				} catch (Exception e) {
					return -1L;
				}
			}
		});
		boolean isUnlock = value!=null && value.equals(DEL_SUCCESS);
		if (isUnlock) {
			//logger.debug("解锁成功, 锁Key:{}", fullKey);
			System.out.println("解锁成功, 锁Key:{"+fullKey+"}");
		}
		return isUnlock;
	}
	/**
	 * 
	 * @Title: get
	 * @Description: TODO(这里用一句话描述这个方法的作用)
	 * @author Administrator
	 * @date 2018-11-22 下午2:38:28
	 * @param key
	 * @return
	 */
	public String get( String key) {
		String obj = null;
		final String fullKey = PREFIX + key;
		try {
			obj = redisTemplate.execute(new RedisCallback<String>() {
				@Override
				public String doInRedis(RedisConnection connection) throws DataAccessException {
					Jedis jedis = (Jedis) connection.getNativeConnection();
					return jedis.get(fullKey);
				}
			});
			String result =  obj;
			return result;
		} catch (Exception e) {
			logger.error("get redis occured an exception", e);
		}
		return "";
	}
	public String getLockId() {
		return lockId;
	}
	
	public static RedisDistributedLock getLock(RedisTemplate<String, Object> redisTemplate) {
		return new RedisDistributedLock(redisTemplate);
	}

	public static RedisDistributedLock getLock(RedisTemplate<String, Object> redisTemplate, int expireSeconds) {
		return new RedisDistributedLock(redisTemplate, expireSeconds, DEFAULT_RETRY_TIME, DEFAULT_INTERNAL_TIME);
	}

	public static RedisDistributedLock getLock(RedisTemplate<String, Object> redisTemplate, int expireSeconds, int retryTimes, int internalTime) {
		return new RedisDistributedLock(redisTemplate, expireSeconds, retryTimes, internalTime);
	}
	
}