1. 程式人生 > >Redis分散式鎖----悲觀鎖實現,以秒殺系統為例

Redis分散式鎖----悲觀鎖實現,以秒殺系統為例

    摘要:本文要實現的是一種使用redis來實現分散式鎖。

1、分散式鎖

    分散式鎖在是一種用來安全訪問分式式機器上變數的安全方案,一般用在全域性id生成,秒殺系統,全域性變數共享、分散式事務等。一般會有兩種實現方案,一種是悲觀鎖的實現,一種是樂觀鎖的實現。悲觀鎖的併發效能差,但是能保證不會發生髒資料的可能性小一點。

2、Redis命令介紹
使用Redis實現分散式鎖,有兩個重要函式需要介紹

SETNX命令(SET if Not eXists)
語法:
SETNX key value
功能:
當且僅當 key 不存在,將 key 的值設為 value ,並返回1;若給定的 key 已經存在,則 SETNX 不做任何動作,並返回0。

GETSET命令(這是一個原子命令!)
語法:
GETSET key value
功能:
將給定 key 的值設為 value ,並返回 key 的舊值 (old value),當 key 存在但不是字串型別時,返回一個錯誤,當key不存在時,返回nil。

GET命令
語法:
GET key
功能:
返回 key 所關聯的字串值,如果 key 不存在那麼返回特殊值 nil 。

DEL命令
語法:
DEL key [KEY …]
功能:
刪除給定的一個或多個 key ,不存在的 key 會被忽略。

3、程式碼實現

(1)AbstractLock基類

package com.github.distribute.lock.redis;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

/**
 * 鎖的骨架實現, 真正的獲取鎖的步驟由子類去實現.
 * 
 *
 */
public abstract class AbstractLock implements Lock {

	/**
	 * <pre>
	 * 這裡需不需要保證可見性值得討論, 因為是分散式的鎖, 
	 * 1.同一個jvm的多個執行緒使用不同的鎖物件其實也是可以的, 這種情況下不需要保證可見性 
	 * 2.同一個jvm的多個執行緒使用同一個鎖物件, 那可見性就必須要保證了.
	 * </pre>
	 */
	protected volatile boolean locked;

	/**
	 * 當前jvm內持有該鎖的執行緒(if have one)
	 */
	private Thread exclusiveOwnerThread;

	public void lock() {
		try {
			lock(false, 0, null, false);
		} catch (InterruptedException e) {
			// TODO ignore
		}
	}

	public void lockInterruptibly() throws InterruptedException {
		lock(false, 0, null, true);
	}

	public boolean tryLock(long time, TimeUnit unit) {
		try {
			System.out.println("ghggggggggggggg");
			return lock(true, time, unit, false);
		} catch (InterruptedException e) {
			e.printStackTrace();
			System.out.println("" + e);
		}
		return false;
	}

	public boolean tryLockInterruptibly(long time, TimeUnit unit) throws InterruptedException {
		return lock(true, time, unit, true);
	}

	public void unlock() {
		// TODO 檢查當前執行緒是否持有鎖
		if (Thread.currentThread() != getExclusiveOwnerThread()) {
			throw new IllegalMonitorStateException("current thread does not hold the lock");
		}

		unlock0();
		setExclusiveOwnerThread(null);
	}

	protected void setExclusiveOwnerThread(Thread thread) {
		exclusiveOwnerThread = thread;
	}

	protected final Thread getExclusiveOwnerThread() {
		return exclusiveOwnerThread;
	}

	protected abstract void unlock0();

	/**
	 * 阻塞式獲取鎖的實現
	 * 
	 * @param useTimeout
	 * @param time
	 * @param unit
	 * @param interrupt
	 *            是否響應中斷
	 * @return
	 * @throws InterruptedException
	 */
	protected abstract boolean lock(boolean useTimeout, long time, TimeUnit unit, boolean interrupt)
			throws InterruptedException;

}

(2)、實現類

package com.github.distribute.lock.redis;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;

import redis.clients.jedis.Jedis;

/**
 * 基於Redis的SETNX操作實現的分散式鎖
 * 
 * 獲取鎖時最好用lock(long time, TimeUnit unit), 以免網路問題而導致執行緒一直阻塞
 */
public class RedisBasedDistributedLock extends AbstractLock {

	private Jedis jedis;

	// 鎖的名字
	protected String lockKey;

	// 鎖的有效時長(毫秒)
	protected long lockExpires;

	public RedisBasedDistributedLock(Jedis jedis, String lockKey, long lockExpires) {
		this.jedis = jedis;
		this.lockKey = lockKey;
		this.lockExpires = lockExpires;
	}

	// 阻塞式獲取鎖的實現
	protected boolean lock(boolean useTimeout, long time, TimeUnit unit, boolean interrupt) throws InterruptedException {
		System.out.println("test1");
		if (interrupt) {
			checkInterruption();
		}

		System.out.println("test2");
		long start = System.currentTimeMillis();
		long timeout = unit.toMillis(time); // if !useTimeout, then it's useless

		while (useTimeout ? isTimeout(start, timeout) : true) {
			System.out.println("test3");
			if (interrupt) {
				checkInterruption();
			}

			long lockExpireTime = System.currentTimeMillis() + lockExpires + 1;// 鎖超時時間
			String stringOfLockExpireTime = String.valueOf(lockExpireTime);

			System.out.println("test4");
			if (jedis.setnx(lockKey, stringOfLockExpireTime) == 1) { // 獲取到鎖
				System.out.println("test5");
				//成功獲取到鎖, 設定相關標識
				locked = true;
				setExclusiveOwnerThread(Thread.currentThread());
				return true;
			}

			System.out.println("test6");
			String value = jedis.get(lockKey);
			if (value != null && isTimeExpired(value)) { // lock is expired
				System.out.println("test7");
				// 假設多個執行緒(非單jvm)同時走到這裡
				String oldValue = jedis.getSet(lockKey, stringOfLockExpireTime); //原子操作
				// 但是走到這裡時每個執行緒拿到的oldValue肯定不可能一樣(因為getset是原子性的)
				// 加入拿到的oldValue依然是expired的,那麼就說明拿到鎖了
				System.out.println("test8");
				if (oldValue != null && isTimeExpired(oldValue)) {
					System.out.println("test9");
					//成功獲取到鎖, 設定相關標識
					locked = true;
					setExclusiveOwnerThread(Thread.currentThread());
					return true;
				}
			} else {
				// TODO lock is not expired, enter next loop retrying
			}
		}
		System.out.println("test10");
		return false;
	}

	public boolean tryLock() {
		long lockExpireTime = System.currentTimeMillis() + lockExpires + 1;// 鎖超時時間
		String stringOfLockExpireTime = String.valueOf(lockExpireTime);

		if (jedis.setnx(lockKey, stringOfLockExpireTime) == 1) { // 獲取到鎖
			// 成功獲取到鎖, 設定相關標識
			locked = true;
			setExclusiveOwnerThread(Thread.currentThread());
			return true;
		}

		String value = jedis.get(lockKey);
		if (value != null && isTimeExpired(value)) { // lock is expired
			// 假設多個執行緒(非單jvm)同時走到這裡
			String oldValue = jedis.getSet(lockKey, stringOfLockExpireTime); //原子操作
			// 但是走到這裡時每個執行緒拿到的oldValue肯定不可能一樣(因為getset是原子性的)
			// 假如拿到的oldValue依然是expired的,那麼就說明拿到鎖了
			if (oldValue != null && isTimeExpired(oldValue)) {
				//成功獲取到鎖, 設定相關標識
				locked = true;
				setExclusiveOwnerThread(Thread.currentThread());
				return true;
			}
		} else {
			// TODO lock is not expired, enter next loop retrying
		}

		return false;
	}

	/**
	 * Queries if this lock is held by any thread.
	 * 
	 * @return {@code true} if any thread holds this lock and {@code false}
	 *         otherwise
	 */
	public boolean isLocked() {
		if (locked) {
			return true;
		} else {
			String value = jedis.get(lockKey);
			// TODO 這裡其實是有問題的, 想:當get方法返回value後, 假設這個value已經是過期的了,
			// 而就在這瞬間, 另一個節點set了value, 這時鎖是被別的執行緒(節點持有), 而接下來的判斷
			// 是檢測不出這種情況的.不過這個問題應該不會導致其它的問題出現, 因為這個方法的目的本來就
			// 不是同步控制, 它只是一種鎖狀態的報告.
			return !isTimeExpired(value);
		}
	}

	@Override
	protected void unlock0() {
		// 判斷鎖是否過期
		String value = jedis.get(lockKey);
		if (!isTimeExpired(value)) {
			doUnlock();
		}
	}

	private void checkInterruption() throws InterruptedException {
		if (Thread.currentThread().isInterrupted()) {
			throw new InterruptedException();
		}
	}

	private boolean isTimeExpired(String value) {
		return Long.parseLong(value) < System.currentTimeMillis();
	}

	private boolean isTimeout(long start, long timeout) {
		return start + timeout > System.currentTimeMillis();
	}

	private void doUnlock() {
		jedis.del(lockKey);
	}

	public Condition newCondition() {
		// TODO Auto-generated method stub
		return null;
	}

}

原理其實很簡單,就是利用setNx和getSet這兩個命令來實現。

SetNx如果返回為1,表示拿到鎖,並設定超時失效時間。

getSet是一個原子操作,它是在鎖超時後沒釋放會進入,這時有可能多個應用一同時進入,但是如果設定成功,會返回oldValue,如果兩個oldvalue一樣,表明拿到鎖了

上面使用到的redis工具類

package com.github.distribute.lock.redis;

import java.util.List;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import redis.clients.jedis.BinaryClient.LIST_POSITION;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

public class RedisUtil {

	private static final Logger LOGGER = LoggerFactory.getLogger(RedisUtil.class);

	private static JedisPool pool = null;

	private static RedisUtil ru = new RedisUtil();

	public static void main(String[] args) {
		RedisUtil redisUtil = RedisUtil.getInstance();
		redisUtil.set("test", "test");
		LOGGER.info(redisUtil.get("test"));
	}

	private RedisUtil() {
		if (pool == null) {
			String ip = "10.75.202.11";
			int port = 6379;
			JedisPoolConfig config = new JedisPoolConfig();
			// 控制一個pool可分配多少個jedis例項,通過pool.getResource()來獲取;
			// 如果賦值為-1,則表示不限制;如果pool已經分配了maxActive個jedis例項,則此時pool的狀態為exhausted(耗盡)。
			config.setMaxTotal(10000);
			// 控制一個pool最多有多少個狀態為idle(空閒的)的jedis例項。
			config.setMaxIdle(2000);
			// 表示當borrow(引入)一個jedis例項時,最大的等待時間,如果超過等待時間,則直接丟擲JedisConnectionException;
			config.setMaxWaitMillis(1000 * 100);
			config.setTestOnBorrow(true);
			pool = new JedisPool(config, ip, port, 100000);
		}

	}

	public Jedis getJedis() {
		Jedis jedis = pool.getResource();
		return jedis;
	}

	public static RedisUtil getInstance() {
		return ru;
	}

	/**
	 * <p>
	 * 通過key獲取儲存在redis中的value
	 * </p>
	 * <p>
	 * 並釋放連線
	 * </p>
	 * 
	 * @param key
	 * @return 成功返回value 失敗返回null
	 */
	public String get(String key) {
		Jedis jedis = null;
		String value = null;
		try {
			jedis = pool.getResource();
			value = jedis.get(key);
		} catch (Exception e) {
			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return value;
	}

	/**
	 * <p>
	 * 向redis存入key和value,並釋放連線資源
	 * </p>
	 * <p>
	 * 如果key已經存在 則覆蓋
	 * </p>
	 * 
	 * @param key
	 * @param value
	 * @return 成功 返回OK 失敗返回 0
	 */
	public String set(String key, String value) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			return jedis.set(key, value);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
			return "0";
		} finally {
			returnResource(pool, jedis);
		}
	}

	/**
	 * <p>
	 * 刪除指定的key,也可以傳入一個包含key的陣列
	 * </p>
	 * 
	 * @param keys
	 *            一個key 也可以使 string 陣列
	 * @return 返回刪除成功的個數
	 */
	public Long del(String... keys) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			return jedis.del(keys);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
			return 0L;
		} finally {
			returnResource(pool, jedis);
		}
	}

	/**
	 * <p>
	 * 通過key向指定的value值追加值
	 * </p>
	 * 
	 * @param key
	 * @param str
	 * @return 成功返回 新增後value的長度 失敗 返回 新增的 value 的長度 異常返回0L
	 */
	public Long append(String key, String str) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.append(key, str);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
			return 0L;
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 判斷key是否存在
	 * </p>
	 * 
	 * @param key
	 * @return true OR false
	 */
	public Boolean exists(String key) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			return jedis.exists(key);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
			return false;
		} finally {
			returnResource(pool, jedis);
		}
	}

	/**
	 * <p>
	 * 設定key value,如果key已經存在則返回0,nx==> not exist
	 * </p>
	 * 
	 * @param key
	 * @param value
	 * @return 成功返回1 如果存在 和 發生異常 返回 0
	 */
	public Long setnx(String key, String value) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			return jedis.setnx(key, value);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
			return 0L;
		} finally {
			returnResource(pool, jedis);
		}
	}

	/**
	 * <p>
	 * 設定key value並制定這個鍵值的有效期
	 * </p>
	 * 
	 * @param key
	 * @param value
	 * @param seconds
	 *            單位:秒
	 * @return 成功返回OK 失敗和異常返回null
	 */
	public String setex(String key, String value, int seconds) {
		Jedis jedis = null;
		String res = null;
		try {
			jedis = pool.getResource();
			res = jedis.setex(key, seconds, value);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key 和offset 從指定的位置開始將原先value替換
	 * </p>
	 * <p>
	 * 下標從0開始,offset表示從offset下標開始替換
	 * </p>
	 * <p>
	 * 如果替換的字串長度過小則會這樣
	 * </p>
	 * <p>
	 * example:
	 * </p>
	 * <p>
	 * value : [email protected]
	 * </p>
	 * <p>
	 * str : abc
	 * </p>
	 * <P>
	 * 從下標7開始替換 則結果為
	 * </p>
	 * <p>
	 * RES : bigsea.abc.cn
	 * </p>
	 * 
	 * @param key
	 * @param str
	 * @param offset
	 *            下標位置
	 * @return 返回替換後 value 的長度
	 */
	public Long setrange(String key, String str, int offset) {
		Jedis jedis = null;
		try {
			jedis = pool.getResource();
			return jedis.setrange(key, offset, str);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
			return 0L;
		} finally {
			returnResource(pool, jedis);
		}
	}

	/**
	 * <p>
	 * 通過批量的key獲取批量的value
	 * </p>
	 * 
	 * @param keys
	 *            string陣列 也可以是一個key
	 * @return 成功返回value的集合, 失敗返回null的集合 ,異常返回空
	 */
	public List<String> mget(String... keys) {
		Jedis jedis = null;
		List<String> values = null;
		try {
			jedis = pool.getResource();
			values = jedis.mget(keys);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return values;
	}

	/**
	 * <p>
	 * 批量的設定key:value,可以一個
	 * </p>
	 * <p>
	 * example:
	 * </p>
	 * <p>
	 * obj.mset(new String[]{"key2","value1","key2","value2"})
	 * </p>
	 * 
	 * @param keysvalues
	 * @return 成功返回OK 失敗 異常 返回 null
	 *
	 */
	public String mset(String... keysvalues) {
		Jedis jedis = null;
		String res = null;
		try {
			jedis = pool.getResource();
			res = jedis.mset(keysvalues);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 批量的設定key:value,可以一個,如果key已經存在則會失敗,操作會回滾
	 * </p>
	 * <p>
	 * example:
	 * </p>
	 * <p>
	 * obj.msetnx(new String[]{"key2","value1","key2","value2"})
	 * </p>
	 * 
	 * @param keysvalues
	 * @return 成功返回1 失敗返回0
	 */
	public Long msetnx(String... keysvalues) {
		Jedis jedis = null;
		Long res = 0L;
		try {
			jedis = pool.getResource();
			res = jedis.msetnx(keysvalues);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 設定key的值,並返回一箇舊值
	 * </p>
	 * 
	 * @param key
	 * @param value
	 * @return 舊值 如果key不存在 則返回null
	 */
	public String getset(String key, String value) {
		Jedis jedis = null;
		String res = null;
		try {
			jedis = pool.getResource();
			res = jedis.getSet(key, value);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過下標 和key 獲取指定下標位置的 value
	 * </p>
	 * 
	 * @param key
	 * @param startOffset
	 *            開始位置 從0 開始 負數表示從右邊開始擷取
	 * @param endOffset
	 * @return 如果沒有返回null
	 */
	public String getrange(String key, int startOffset, int endOffset) {
		Jedis jedis = null;
		String res = null;
		try {
			jedis = pool.getResource();
			res = jedis.getrange(key, startOffset, endOffset);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key 對value進行加值+1操作,當value不是int型別時會返回錯誤,當key不存在是則value為1
	 * </p>
	 * 
	 * @param key
	 * @return 加值後的結果
	 */
	public Long incr(String key) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.incr(key);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key給指定的value加值,如果key不存在,則這是value為該值
	 * </p>
	 * 
	 * @param key
	 * @param integer
	 * @return
	 */
	public Long incrBy(String key, Long integer) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.incrBy(key, integer);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 對key的值做減減操作,如果key不存在,則設定key為-1
	 * </p>
	 * 
	 * @param key
	 * @return
	 */
	public Long decr(String key) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.decr(key);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 減去指定的值
	 * </p>
	 * 
	 * @param key
	 * @param integer
	 * @return
	 */
	public Long decrBy(String key, Long integer) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.decrBy(key, integer);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key獲取value值的長度
	 * </p>
	 * 
	 * @param key
	 * @return 失敗返回null
	 */
	public Long serlen(String key) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.strlen(key);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key給field設定指定的值,如果key不存在,則先建立
	 * </p>
	 * 
	 * @param key
	 * @param field
	 *            欄位
	 * @param value
	 * @return 如果存在返回0 異常返回null
	 */
	public Long hset(String key, String field, String value) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.hset(key, field, value);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key給field設定指定的值,如果key不存在則先建立,如果field已經存在,返回0
	 * </p>
	 * 
	 * @param key
	 * @param field
	 * @param value
	 * @return
	 */
	public Long hsetnx(String key, String field, String value) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.hsetnx(key, field, value);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key同時設定 hash的多個field
	 * </p>
	 * 
	 * @param key
	 * @param hash
	 * @return 返回OK 異常返回null
	 */
	public String hmset(String key, Map<String, String> hash) {
		Jedis jedis = null;
		String res = null;
		try {
			jedis = pool.getResource();
			res = jedis.hmset(key, hash);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key 和 field 獲取指定的 value
	 * </p>
	 * 
	 * @param key
	 * @param field
	 * @return 沒有返回null
	 */
	public String hget(String key, String field) {
		Jedis jedis = null;
		String res = null;
		try {
			jedis = pool.getResource();
			res = jedis.hget(key, field);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key 和 fields 獲取指定的value 如果沒有對應的value則返回null
	 * </p>
	 * 
	 * @param key
	 * @param fields
	 *            可以使 一個String 也可以是 String陣列
	 * @return
	 */
	public List<String> hmget(String key, String... fields) {
		Jedis jedis = null;
		List<String> res = null;
		try {
			jedis = pool.getResource();
			res = jedis.hmget(key, fields);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key給指定的field的value加上給定的值
	 * </p>
	 * 
	 * @param key
	 * @param field
	 * @param value
	 * @return
	 */
	public Long hincrby(String key, String field, Long value) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.hincrBy(key, field, value);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key和field判斷是否有指定的value存在
	 * </p>
	 * 
	 * @param key
	 * @param field
	 * @return
	 */
	public Boolean hexists(String key, String field) {
		Jedis jedis = null;
		Boolean res = false;
		try {
			jedis = pool.getResource();
			res = jedis.hexists(key, field);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key返回field的數量
	 * </p>
	 * 
	 * @param key
	 * @return
	 */
	public Long hlen(String key) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.hlen(key);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;

	}

	/**
	 * <p>
	 * 通過key 刪除指定的 field
	 * </p>
	 * 
	 * @param key
	 * @param fields
	 *            可以是 一個 field 也可以是 一個數組
	 * @return
	 */
	public Long hdel(String key, String... fields) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.hdel(key, fields);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key返回所有的field
	 * </p>
	 * 
	 * @param key
	 * @return
	 */
	public Set<String> hkeys(String key) {
		Jedis jedis = null;
		Set<String> res = null;
		try {
			jedis = pool.getResource();
			res = jedis.hkeys(key);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key返回所有和key有關的value
	 * </p>
	 * 
	 * @param key
	 * @return
	 */
	public List<String> hvals(String key) {
		Jedis jedis = null;
		List<String> res = null;
		try {
			jedis = pool.getResource();
			res = jedis.hvals(key);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key獲取所有的field和value
	 * </p>
	 * 
	 * @param key
	 * @return
	 */
	public Map<String, String> hgetall(String key) {
		Jedis jedis = null;
		Map<String, String> res = null;
		try {
			jedis = pool.getResource();
			res = jedis.hgetAll(key);
		} catch (Exception e) {
			// TODO
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key向list頭部新增字串
	 * </p>
	 * 
	 * @param key
	 * @param strs
	 *            可以使一個string 也可以使string陣列
	 * @return 返回list的value個數
	 */
	public Long lpush(String key, String... strs) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.lpush(key, strs);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key向list尾部新增字串
	 * </p>
	 * 
	 * @param key
	 * @param strs
	 *            可以使一個string 也可以使string陣列
	 * @return 返回list的value個數
	 */
	public Long rpush(String key, String... strs) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.rpush(key, strs);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key在list指定的位置之前或者之後 新增字串元素
	 * </p>
	 * 
	 * @param key
	 * @param where
	 *            LIST_POSITION列舉型別
	 * @param pivot
	 *            list裡面的value
	 * @param value
	 *            新增的value
	 * @return
	 */
	public Long linsert(String key, LIST_POSITION where, String pivot, String value) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.linsert(key, where, pivot, value);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key設定list指定下標位置的value
	 * </p>
	 * <p>
	 * 如果下標超過list裡面value的個數則報錯
	 * </p>
	 * 
	 * @param key
	 * @param index
	 *            從0開始
	 * @param value
	 * @return 成功返回OK
	 */
	public String lset(String key, Long index, String value) {
		Jedis jedis = null;
		String res = null;
		try {
			jedis = pool.getResource();
			res = jedis.lset(key, index, value);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key從對應的list中刪除指定的count個 和 value相同的元素
	 * </p>
	 * 
	 * @param key
	 * @param count
	 *            當count為0時刪除全部
	 * @param value
	 * @return 返回被刪除的個數
	 */
	public Long lrem(String key, long count, String value) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.lrem(key, count, value);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key保留list中從strat下標開始到end下標結束的value值
	 * </p>
	 * 
	 * @param key
	 * @param start
	 * @param end
	 * @return 成功返回OK
	 */
	public String ltrim(String key, long start, long end) {
		Jedis jedis = null;
		String res = null;
		try {
			jedis = pool.getResource();
			res = jedis.ltrim(key, start, end);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key從list的頭部刪除一個value,並返回該value
	 * </p>
	 * 
	 * @param key
	 * @return
	 */
	synchronized public String lpop(String key) {
		Jedis jedis = null;
		String res = null;
		try {
			jedis = pool.getResource();
			res = jedis.lpop(key);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key從list尾部刪除一個value,並返回該元素
	 * </p>
	 * 
	 * @param key
	 * @return
	 */
	synchronized public String rpop(String key) {
		Jedis jedis = null;
		String res = null;
		try {
			jedis = pool.getResource();
			res = jedis.rpop(key);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key從一個list的尾部刪除一個value並新增到另一個list的頭部,並返回該value
	 * </p>
	 * <p>
	 * 如果第一個list為空或者不存在則返回null
	 * </p>
	 * 
	 * @param srckey
	 * @param dstkey
	 * @return
	 */
	public String rpoplpush(String srckey, String dstkey) {
		Jedis jedis = null;
		String res = null;
		try {
			jedis = pool.getResource();
			res = jedis.rpoplpush(srckey, dstkey);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key獲取list中指定下標位置的value
	 * </p>
	 * 
	 * @param key
	 * @param index
	 * @return 如果沒有返回null
	 */
	public String lindex(String key, long index) {
		Jedis jedis = null;
		String res = null;
		try {
			jedis = pool.getResource();
			res = jedis.lindex(key, index);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key返回list的長度
	 * </p>
	 * 
	 * @param key
	 * @return
	 */
	public Long llen(String key) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.llen(key);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key獲取list指定下標位置的value
	 * </p>
	 * <p>
	 * 如果start 為 0 end 為 -1 則返回全部的list中的value
	 * </p>
	 * 
	 * @param key
	 * @param start
	 * @param end
	 * @return
	 */
	public List<String> lrange(String key, long start, long end) {
		Jedis jedis = null;
		List<String> res = null;
		try {
			jedis = pool.getResource();
			res = jedis.lrange(key, start, end);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key向指定的set中新增value
	 * </p>
	 * 
	 * @param key
	 * @param members
	 *            可以是一個String 也可以是一個String陣列
	 * @return 新增成功的個數
	 */
	public Long sadd(String key, String... members) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.sadd(key, members);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key刪除set中對應的value值
	 * </p>
	 * 
	 * @param key
	 * @param members
	 *            可以是一個String 也可以是一個String陣列
	 * @return 刪除的個數
	 */
	public Long srem(String key, String... members) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.srem(key, members);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key隨機刪除一個set中的value並返回該值
	 * </p>
	 * 
	 * @param key
	 * @return
	 */
	public String spop(String key) {
		Jedis jedis = null;
		String res = null;
		try {
			jedis = pool.getResource();
			res = jedis.spop(key);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key獲取set中的差集
	 * </p>
	 * <p>
	 * 以第一個set為標準
	 * </p>
	 * 
	 * @param keys
	 *            可以使一個string 則返回set中所有的value 也可以是string陣列
	 * @return
	 */
	public Set<String> sdiff(String... keys) {
		Jedis jedis = null;
		Set<String> res = null;
		try {
			jedis = pool.getResource();
			res = jedis.sdiff(keys);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key獲取set中的差集並存入到另一個key中
	 * </p>
	 * <p>
	 * 以第一個set為標準
	 * </p>
	 * 
	 * @param dstkey
	 *            差集存入的key
	 * @param keys
	 *            可以使一個string 則返回set中所有的value 也可以是string陣列
	 * @return
	 */
	public Long sdiffstore(String dstkey, String... keys) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.sdiffstore(dstkey, keys);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key獲取指定set中的交集
	 * </p>
	 * 
	 * @param keys
	 *            可以使一個string 也可以是一個string陣列
	 * @return
	 */
	public Set<String> sinter(String... keys) {
		Jedis jedis = null;
		Set<String> res = null;
		try {
			jedis = pool.getResource();
			res = jedis.sinter(keys);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key獲取指定set中的交集 並將結果存入新的set中
	 * </p>
	 * 
	 * @param dstkey
	 * @param keys
	 *            可以使一個string 也可以是一個string陣列
	 * @return
	 */
	public Long sinterstore(String dstkey, String... keys) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.sinterstore(dstkey, keys);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key返回所有set的並集
	 * </p>
	 * 
	 * @param keys
	 *            可以使一個string 也可以是一個string陣列
	 * @return
	 */
	public Set<String> sunion(String... keys) {
		Jedis jedis = null;
		Set<String> res = null;
		try {
			jedis = pool.getResource();
			res = jedis.sunion(keys);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key返回所有set的並集,並存入到新的set中
	 * </p>
	 * 
	 * @param dstkey
	 * @param keys
	 *            可以使一個string 也可以是一個string陣列
	 * @return
	 */
	public Long sunionstore(String dstkey, String... keys) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.sunionstore(dstkey, keys);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key將set中的value移除並新增到第二個set中
	 * </p>
	 * 
	 * @param srckey
	 *            需要移除的
	 * @param dstkey
	 *            新增的
	 * @param member
	 *            set中的value
	 * @return
	 */
	public Long smove(String srckey, String dstkey, String member) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.smove(srckey, dstkey, member);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key獲取set中value的個數
	 * </p>
	 * 
	 * @param key
	 * @return
	 */
	public Long scard(String key) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.scard(key);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key判斷value是否是set中的元素
	 * </p>
	 * 
	 * @param key
	 * @param member
	 * @return
	 */
	public Boolean sismember(String key, String member) {
		Jedis jedis = null;
		Boolean res = null;
		try {
			jedis = pool.getResource();
			res = jedis.sismember(key, member);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key獲取set中隨機的value,不刪除元素
	 * </p>
	 * 
	 * @param key
	 * @return
	 */
	public String srandmember(String key) {
		Jedis jedis = null;
		String res = null;
		try {
			jedis = pool.getResource();
			res = jedis.srandmember(key);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key獲取set中所有的value
	 * </p>
	 * 
	 * @param key
	 * @return
	 */
	public Set<String> smembers(String key) {
		Jedis jedis = null;
		Set<String> res = null;
		try {
			jedis = pool.getResource();
			res = jedis.smembers(key);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key向zset中新增value,score,其中score就是用來排序的
	 * </p>
	 * <p>
	 * 如果該value已經存在則根據score更新元素
	 * </p>
	 * 
	 * @param key
	 * @param score
	 * @param member
	 * @return
	 */
	public Long zadd(String key, double score, String member) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.zadd(key, score, member);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key刪除在zset中指定的value
	 * </p>
	 * 
	 * @param key
	 * @param members
	 *            可以使一個string 也可以是一個string陣列
	 * @return
	 */
	public Long zrem(String key, String... members) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.zrem(key, members);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key增加該zset中value的score的值
	 * </p>
	 * 
	 * @param key
	 * @param score
	 * @param member
	 * @return
	 */
	public Double zincrby(String key, double score, String member) {
		Jedis jedis = null;
		Double res = null;
		try {
			jedis = pool.getResource();
			res = jedis.zincrby(key, score, member);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key返回zset中value的排名
	 * </p>
	 * <p>
	 * 下標從小到大排序
	 * </p>
	 * 
	 * @param key
	 * @param member
	 * @return
	 */
	public Long zrank(String key, String member) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.zrank(key, member);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key返回zset中value的排名
	 * </p>
	 * <p>
	 * 下標從大到小排序
	 * </p>
	 * 
	 * @param key
	 * @param member
	 * @return
	 */
	public Long zrevrank(String key, String member) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.zrevrank(key, member);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key將獲取score從start到end中zset的value
	 * </p>
	 * <p>
	 * socre從大到小排序
	 * </p>
	 * <p>
	 * 當start為0 end為-1時返回全部
	 * </p>
	 * 
	 * @param key
	 * @param start
	 * @param end
	 * @return
	 */
	public Set<String> zrevrange(String key, long start, long end) {
		Jedis jedis = null;
		Set<String> res = null;
		try {
			jedis = pool.getResource();
			res = jedis.zrevrange(key, start, end);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key返回指定score內zset中的value
	 * </p>
	 * 
	 * @param key
	 * @param max
	 * @param min
	 * @return
	 */
	public Set<String> zrangebyscore(String key, String max, String min) {
		Jedis jedis = null;
		Set<String> res = null;
		try {
			jedis = pool.getResource();
			res = jedis.zrevrangeByScore(key, max, min);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key返回指定score內zset中的value
	 * </p>
	 * 
	 * @param key
	 * @param max
	 * @param min
	 * @return
	 */
	public Set<String> zrangeByScore(String key, double max, double min) {
		Jedis jedis = null;
		Set<String> res = null;
		try {
			jedis = pool.getResource();
			res = jedis.zrevrangeByScore(key, max, min);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 返回指定區間內zset中value的數量
	 * </p>
	 * 
	 * @param key
	 * @param min
	 * @param max
	 * @return
	 */
	public Long zcount(String key, String min, String max) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.zcount(key, min, max);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key返回zset中的value個數
	 * </p>
	 * 
	 * @param key
	 * @return
	 */
	public Long zcard(String key) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.zcard(key);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key獲取zset中value的score值
	 * </p>
	 * 
	 * @param key
	 * @param member
	 * @return
	 */
	public Double zscore(String key, String member) {
		Jedis jedis = null;
		Double res = null;
		try {
			jedis = pool.getResource();
			res = jedis.zscore(key, member);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key刪除給定區間內的元素
	 * </p>
	 * 
	 * @param key
	 * @param start
	 * @param end
	 * @return
	 */
	public Long zremrangeByRank(String key, long start, long end) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.zremrangeByRank(key, start, end);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key刪除指定score內的元素
	 * </p>
	 * 
	 * @param key
	 * @param start
	 * @param end
	 * @return
	 */
	public Long zremrangeByScore(String key, double start, double end) {
		Jedis jedis = null;
		Long res = null;
		try {
			jedis = pool.getResource();
			res = jedis.zremrangeByScore(key, start, end);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 返回滿足pattern表示式的所有key
	 * </p>
	 * <p>
	 * keys(*)
	 * </p>
	 * <p>
	 * 返回所有的key
	 * </p>
	 * 
	 * @param pattern
	 * @return
	 */
	public Set<String> keys(String pattern) {
		Jedis jedis = null;
		Set<String> res = null;
		try {
			jedis = pool.getResource();
			res = jedis.keys(pattern);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * <p>
	 * 通過key判斷值得型別
	 * </p>
	 * 
	 * @param key
	 * @return
	 */
	public String type(String key) {
		Jedis jedis = null;
		String res = null;
		try {
			jedis = pool.getResource();
			res = jedis.type(key);
		} catch (Exception e) {

			LOGGER.error(e.getMessage());
		} finally {
			returnResource(pool, jedis);
		}
		return res;
	}

	/**
	 * 返還到連線池
	 *
	 * @param pool
	 * @param jedis
	 */
	public static void returnResource(JedisPool pool, Jedis jedis) {
		if (jedis != null) {
			pool.returnResourceObject(jedis);
		}
	}

	/**
	 * 返還到連線池
	 *
	 * @param pool
	 * @param jedis
	 */
	public static void returnResource(Jedis jedis) {
		if (jedis != null) {
			pool.returnResourceObject(jedis);
		}
	}
}


4、測試

寫了一個簡單的秒殺系統的模擬

package com.github.distribute.lock.redis;

import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import redis.clients.jedis.Jedis;

public class PessimisticLockTest {

	public static void main(String[] args) {
		long starTime=System.currentTimeMillis();
		
		initPrduct();
		initClient();
		printResult();
		 
		long endTime=System.currentTimeMillis();
		long Time=endTime-starTime;
		System.out.println("程式執行時間: "+Time+"ms");   
	}

	/**
	 * 輸出結果
	 */
	public static void printResult() {
		Jedis jedis = RedisUtil.getInstance().getJedis();
		Set<String> set = jedis.smembers("clientList");

		int i = 1;
		for (String value : set) {
			System.out.println("第" + i++ + "個搶到商品," + value + " ");
		}

		RedisUtil.returnResource(jedis);
	}

	/*
	 * 初始化顧客開始搶商品
	 */
	public static void initClient() {
		ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
		int clientNum = 10000;// 模擬客戶數目
		for (int i = 0; i < clientNum; i++) {
			cachedThreadPool.execute(new PessClientThread(i));
		}
		cachedThreadPool.shutdown();

		while (true) {
			if (cachedThreadPool.isTerminated()) {
				System.out.println("所有的執行緒都結束了!");
				break;
			}
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 初始化商品個數
	 */
	public static void initPrduct() {
		int prdNum = 100;// 商品個數
		String key = "prdNum";
		String clientList = "clientList";// 搶購到商品的顧客列表
		Jedis jedis = RedisUtil.getInstance().getJedis();

		if (jedis.exists(key)) {
			jedis.del(key);
		}

		if (jedis.exists(clientList)) {
			jedis.del(clientList);
		}

		jedis.set(key, String.valueOf(prdNum));// 初始化
		RedisUtil.returnResource(jedis);
	}

}

/**
 * 顧客執行緒
 * 
 * @author linbingwen
 *
 */
class PessClientThread implements Runnable {
	String key = "prdNum";// 商品主鍵
	String clientList = "clientList";// // 搶購到商品的顧客列表主鍵
	String clientName;
	RedisBasedDistributedLock redisBasedDistributedLock;
	Jedis jedis = null;

	public PessClientThread(int num) {
		clientName = "編號=" + num;
		init();
	}

	public void init() {
		jedis = RedisUtil.getInstance().getJedis();
		redisBasedDistributedLock = new RedisBasedDistributedLock(jedis, "lock.lock", 5 * 1000);
	}

	public void run() {
		try {
			Thread.sleep((int) (Math.random() * 5000));// 隨機睡眠一下
		} catch (InterruptedException e1) {
		}

		while (true) {
			//先判斷快取是否有商品
			if(Integer.valueOf(jedis.get(key))<= 0) {
				break;
			}
			
			//快取還有商品,取鎖,商品數目減去1
			System.out.println("顧客:" + clientName + "開始搶商品");
			if (redisBasedDistributedLock.tryLock(3,TimeUnit.SECONDS)) { //等待3秒獲取鎖,否則返回false
				int prdNum = Integer.valueOf(jedis.get(key)); //再次取得商品快取數目
				if (prdNum > 0) {
					jedis.decr(key);//商品數減1
					jedis.sadd(clientList, clientName);// 搶到商品記錄一下
					System.out.println("好高興,顧客:" + clientName + "搶到商品");
				} else {
					System.out.println("悲劇了,庫存為0,顧客:" + clientName + "沒有搶到商品");
				}
				redisBasedDistributedLock.unlock();
				break;
			}
		}
		//釋放資源
		redisBasedDistributedLock = null;
		RedisUtil.returnResource(jedis);
	}

}


輸出結果:

更多技術細節請關注的筆者的公眾號"單例模式":

相關推薦

Redis分散式----悲觀實現,系統

    摘要:本文要實現的是一種使用redis來實現分散式鎖。 1、分散式鎖     分散式鎖在是一種用來安全訪問分式式機器上變數的安全方案,一般用在全域性id生成,秒殺系統,全域性變數共享、分散式事務等。一般會有兩種實現方案,一種是悲觀鎖的實現,一種是樂觀鎖的實現。

Redis分散式----樂觀實現系統

     摘要:本文使用redis來實現樂觀鎖,並以秒殺系統為例項來講解整個過程。 樂觀鎖       大多數是基於資料版本(version)的記錄機制實現的。即為資料增加一個版本標識,在基於資料庫表的版本解決方案中,一般是通過為資料庫表增加一個”version”欄位來

Redis 分散式:樂觀實現系統

樂觀鎖大多數是基於資料版本(version)的記錄機制實現的。即為資料增加一個版本標識,在基於資料庫表的版本解決方案中,一般是通過為資料庫表增加一個”version”欄位來實現讀取出資料時,將此版本號一同讀出,之後更新時,對此版本號加1。此時,將提交資料的版本號與資料庫表對應

基於redis實現商品系統

利用redis的樂觀鎖,實現秒殺系統的資料同步(基於watch實現) 使用者一: import redis conn = redis.Redis(host='127.0.0.1',port=6379) # conn.set('count',1000) with conn.pipeline()

解密 Redis 助力雙十一背後電商系統

秒殺活動是絕大部分電商選擇的低價促銷、推廣品牌的方式。不僅可以給平臺帶來使用者量,還可以提高平臺知名度。一個好的秒殺系統,可以提高平臺系統的穩定性和公平性,獲得更好的使用者體驗,提升平臺的口碑,從而提升秒殺活動的最大價值。 本文討論如何利用 Redis 快取設計高併發的秒殺系統。 秒殺的特徵 秒殺活動對

堆排序演算法及其Java實現(大根堆)

(二叉)堆資料結構是一種陣列物件,如圖所示(下標從0開始),它完全可以被視為一棵完全二叉樹。 接下來要出現的幾個詞語,這裡介紹一下: length[A]: 陣列A中元素的個數 heap-size[A]: 存放在陣列A中堆的元素的個數,是要排序的元素的個

【C語言】位元組對齊問題(32位系統

  1. 什麼是對齊?   現代計算機中記憶體空間都是按照位元組(byte)劃分的,從理論上講似乎對任何型別的變數的訪問可以從任何地址開始,但實際情況是在訪問特定變數的時候經常在特定的記憶體地址訪問,這就需要各型別資料按照一定的規則在空間上排列,而不是順序地一個接一個地排放,這就是對齊

UML建模語言7種圖(銀行ATM系統)

1 用例圖:       描述了系統提供的一個功能單元。以一種視覺化的方式理解系統的功能需求,"角色"與系統內用例之間的關係。       本例中,參與者"銀行儲戶"和ATM機。簡化後的ATM機僅有取款、存款及其餘功能。其餘功能不做詳細說明。 2 類圖:        顯示系

Redis分散式 實現系統 SET命令實現

基於Redis命令:SET key valueNX EX max-lock-time   可適用於redis單機和redis叢集模式 1.SET命令是原子性操作,NX指令保證只要當key不存在時才會設定value 2.設定的value要有唯一性,來確保鎖不會被誤刪(

Redis分散式實現簡單功能

這版秒殺只是解決瞬間訪問過高伺服器壓力過大,請求速度變慢,大大消耗伺服器效能的問題。 主要就是在高併發秒殺的場景下,很多人訪問時並沒有拿到鎖,所以直接跳過了。這樣就處理了多執行緒併發問題的同時也保證了伺服器的效能的穩定。 接下來我們使用redis的分散式鎖來進行枷鎖處理: 我們可以在進入下單的方法後將核

商品超賣講解Redis分散式

本案例主要講解Redis實現分散式鎖的兩種實現方式:Jedis實現、Redisson實現。網上關於這方面講解太多了,Van自認為文筆沒他們好,還是用示例程式碼說明。 一、jedis 實現 該方案只考慮Redis單機部署的場景 1.1 加鎖 1.1.1 原理 jedis.set(String key,

redis樂觀(適用於系統

修改 導致 代碼 -a 通知 解決 redis服務器 font 變化 redis事務中的WATCH命令和基於CAS的樂觀鎖 在Redis的事務中,WATCH命令可用於提供CAS(check-and-set)功能。假設我們通過WATCH命令在事務執行之前監控了多個Keys,

Redis輕松實現系統

tar 幫助 說過 腳本 .net 所有 paxos 你會 用戶 秒殺系統的架構設計 秒殺系統,是典型的短時大量突發訪問類問題。對這類問題,有三種優化性能的思路: 寫入內存而不是寫入硬盤 異步處理而不是同步處理 分布式處理 用上這三招,不論秒殺時負載多大,都能輕松應對。

crontab實現執行

crontab 秒級linux中crontab實現以秒執行任務很多時候,我們計劃任務需要精確到秒來執行,根據以下方法,可以很容易地以秒執行任務。以下方法將每10秒執行一次1.編輯crontabcrontab -e * * * * * /bin/date >>/tmp/date.txt * * *

從構建分布式系統聊聊Lock使用中的坑

Lock 事物 秒殺 前言 在單體架構的秒殺活動中,為了減輕DB層的壓力,這裏我們采用了Lock鎖來實現秒殺用戶排隊搶購。然而很不幸的是盡管使用了鎖,但是測試過程中仍然會超賣,執行了N多次發現依然有問題。輸出一下代碼吧,可能大家看的比較真切: @Service("seckillService")

從構建分布式系統聊聊分布式

調用 you tel bre policy 學習 感悟 ant 操作 前言 最近懶成一坨屎,學不動系列一波接一波,大多還都是底層原理相關的。上周末抽時間重讀了周誌明大濕的 JVM 高效並發部分,每讀一遍都有不同的感悟。路漫漫,借此,把前段時間搞著玩的秒殺案例中的分布式鎖深

使用Redis+MQ+MySql實現商品

並發 緩存 高速緩存 數據處理 數據 執行時間 判斷 但是 操作 基本思路是通過Redis高速緩存來代替緩慢的數據庫操作,借此提高系統在高並發情況下的數據處理能力。原系統:1)秒殺請求進入後臺後被發送到消息隊列,並返回入隊成功;2)消息隊列的消費者依次處理執行SQL處理消息

AtomicInteger如何保證執行緒安全以及樂觀/悲觀的概念

最近面試被問到一個問題,AtomicInteger如何保證執行緒安全?我查閱了資料 發現還可以引申到 樂觀鎖/悲觀鎖的概念,覺得值得一記。 眾所周知,JDK提供了AtomicInteger保證對數字的操作是執行緒安全的,執行緒安全我首先想到了synchronized和Lock,但是這

樂觀悲觀,CAS,volatile

悲觀鎖 假設最壞的情況,每次去讀取資料都認為別人會修改資料,所以可能會產生併發,於是每次在拿資料的時候都要上鎖。Java裡面的同步原語synchronized關鍵字的實現也是悲觀鎖。 樂觀鎖 就是每次拿資料的時候認為不會有人來修改資料,所以不上鎖,但是在更新的時候會判斷此期間是否

事務 悲觀 樂觀 概念 應用場景 使用方式 小記

【部落格園cnblogs筆者m-yb原創(部分引用, 在文末有註明),轉載請加本文部落格連結,筆者github: https://github.com/mayangbo666,公眾號aandb7,QQ群927113708】  https://www.cnblogs.com/m-yb/p/99749