Redis分布式锁实现类

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);
	}
	
}

版权声明:著作权归作者所有。

相关推荐

MySQL类型隐式转换规则

在MySQL语句里,如果操作符应用于不同的类型的操作数,为了兼容让操作数兼容,MySQL会对操作数做类型转换,有些是自动的隐式转换。“+”号操作符使用“+”号相加的是字符串或字符串和数字,字符串会隐式转换为数字。以数字开头的字符串以数字开头的字符串,它会截取前面的数字字符串,转换为对应的数字。mysql> select '1a' + 1; +-----

Kotlin:类的定义

基本定义Kotlin使用关键词class定义类,如:class User { } 声明类主要包括三部分:类名:必选,类的名称,一般以大写字母开头。类头:可选,类头包括type parameter(如泛型),主构造(primary constructor)等。类体:可选,在Kotlin,类体是可选的,它有大括号{}括起来。类头和类体是可选的,一个最简单的类可

[译]使用JDK 9 Flow API进行响应式编程

什么是响应式编程?响应式编程是关于处理数据项的异步流,也就是应用程序在数据项发生时对其进行响应。 数据流实质上是指随时间发生的数据项序列。与迭代内存数据相比, 这个模型的内存效率更高,因为数据是以流的形式处理的。在响应式编程模型中,有一个Publisher和一个Subscriber。 Publisher发布一个数据流,Subscriber异步订阅。该模型还提供了一种机

Kotlin实现Java的三元条件运算

在Kotlin没有类似Java的三元条件运算:a ? b : c 在Kotlin有几种等效的方法:ifval r =if (a) b else c whenval r = when(a) {     true 

Android使用CountDownTimer实现倒计时示例(Kotlin)

CountDownTimer是一个抽象类,它的构造函数为CountDownTimer(long millisInFuture, long countDownInterval) millisInFuture:倒计时的总时间,从调用start()方法开始。毫秒数countDownInterval:倒计时的时间间隔。毫秒数。CountDownTimer主要有四个方法:syn