基于redis的分布式锁

参考 : https://blog.youkuaiyun.com/tzszhzx/article/details/46363491 

package com.gaojiasoft.gaojiaRedis;

import java.util.UUID;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.annotation.PostConstruct;

import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.exceptions.JedisConnectionException;

/**
 * 分布式锁管理器,支持对单个资源加锁解锁,或给一批资源的批量加锁及解锁
 *  
 */
public class DistributedLockManger {

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

	private static final int DEFAULT_SINGLE_EXPIRE_TIME = 3;

	// private static final int DEFAULT_BATCH_EXPIRE_TIME = 6;

	// static的变量无法注解
	@Autowired
	JedisCluster jc;

	private static DistributedLockManger lockManger;

	public DistributedLockManger() {
	}

	@PostConstruct
	public void init() {
		lockManger = this;
		lockManger.jc = this.jc;
	}

	/**
	 * 获取锁 如果锁可用 立即返回true, 否则立即返回false,作为非阻塞式锁使用
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public static boolean tryLock(String key, String value) {
		try {
			return tryLock(key, value, 0L, null);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return false;
	}

	/**
	 * 锁在给定的等待时间内空闲,则获取锁成功 返回true, 否则返回false,作为阻塞式锁使用
	 * 
	 * @param key
	 *            锁键
	 * @param value
	 *            被谁锁定
	 * @param timeout
	 *            尝试获取锁时长,建议传递500,结合实践单位,则可表示500毫秒
	 * @param unit,建议传递TimeUnit.MILLISECONDS
	 * @return
	 * @throws InterruptedException
	 */
	public static boolean tryLock(String key, String value, long timeout, TimeUnit unit) throws InterruptedException {
		// 纳秒
		long begin = System.nanoTime();
		do {
			// LOGGER.debug("{}尝试获得{}的锁.", value, key);
			Long i = lockManger.jc.setnx(key, value);
			if (i == 1) {
				lockManger.jc.expire(key, DEFAULT_SINGLE_EXPIRE_TIME);
				LOGGER.debug("{}成功获取{}的锁,设置锁过期时间为{}秒 ", value, key, DEFAULT_SINGLE_EXPIRE_TIME);
				return true;
			} else {
				// 存在锁 ,但可能获取不到,原因是获取的一刹那间
				// String desc = lockManger.jc.get(key);
				// LOGGER.error("{}正被{}锁定.", key, desc);
			}
			if (timeout == 0) {
				break;
			}
			// 在其睡眠的期间,锁可能被解,也可能又被他人占用,但会尝试继续获取锁直到指定的时间
			Thread.sleep(100);
		} while ((System.nanoTime() - begin) < unit.toNanos(timeout));
		// 因超时没有获得锁
		return false;
	}

	/**
	 * 释放单个锁
	 * 
	 * @param key
	 *            锁键
	 * @param value
	 *            被谁释放
	 */
	public static void unLock(String key, String value) {
		try {
			lockManger.jc.del(key);
			LOGGER.debug("{}锁被{}释放 .", key, value);
		} catch (JedisConnectionException je) {

		} catch (Exception e) {

		}
	}

	public void test() throws InterruptedException {
		String productId = "18061249844";
		String userId;
		for (int i = 1; i <= 500; i++) {
			// 随机产生一个用户
			userId = UUID.randomUUID().toString();
			// 该用户试图锁定(如果被锁,尝试等待300毫秒),在处理一些事情后,再释放锁
			testLock(productId, userId);
			Thread.sleep(20);
		}
	}

	private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(
			150, 150, 30L, TimeUnit.SECONDS,
			new LinkedBlockingQueue<Runnable>(),
			new BasicThreadFactory.Builder().daemon(true).namingPattern("mongo-oper-%d").build(),
			new ThreadPoolExecutor.CallerRunsPolicy());

	private static void testLock(final String key, final String value) {
		executor.execute(new Runnable() {
			@Override
			public void run() {
				try {
					// 获取锁,如果不能立即获取,尝试等待1000毫秒
					boolean isLock = DistributedLockManger.tryLock(key, value, 500, TimeUnit.MILLISECONDS);
					if (isLock) {
						// doSomething(占用锁20毫秒到4秒,模拟处理事务)
						long doSomeThingTime = RandomUtils.nextLong(20, 4000);
						LOGGER.debug("{}将持有锁{}时长{}毫秒.", value, key, doSomeThingTime);
						Thread.sleep(doSomeThingTime);
						// 然后释放锁
						DistributedLockManger.unLock(key, value);
					}
				} catch (Throwable th) {
				}
			}
		});
	}

	public JedisCluster getJc() {
		return jc;
	}

	public void setJc(JedisCluster jc) {
		this.jc = jc;
	}
}


使用redis完成分布式锁,与使用zookeeper完成分布式锁的区别:

     zookeeper的方案最大的优势在于避免结点挂掉后导致的死锁;

    redis的方案最大的优势在于性能超强;在实际生产过程中,结合自身情况来决定最适合的分布式锁。


锁的类型:

    乐观锁

    悲观锁


参考文章:

    Redis分布式锁----乐观锁的实现,以秒杀系统为例

    Redis分布式锁----悲观锁实现,以秒杀系统为例

    Zookeeper分布式锁实现


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值