侧边栏壁纸
博主头像
Terry

『LESSON 5』

  • 累计撰写 90 篇文章
  • 累计创建 21 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

Redis的分布式锁实现

Terry
2020-06-06 / 0 评论 / 0 点赞 / 530 阅读 / 3,575 字 / 正在检测是否收录...
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.data.redis.serializer.RedisSerializer;
import saas.common.util.RedisKeyUtils;

import java.util.concurrent.TimeUnit;

/**
 * 基于线程本地实现的线程级简单锁
 *
 * @author Terry
 */
public class OrderLockHelper {

    private static final Logger logger = LoggerFactory.getLogger(OrderLockHelper.class);

    /**
     * 资源ID前缀
     */
    private static final String RESOURCE_ID_PREFIX = "orderLock";
    /**
     * 默认超时时间 60s
     */
    private static final int DEFAULT_TIMEOUT_SECONDS = 60;

    /**
     * Owner's lock threadLocal
     */
    private static final ThreadLocal<String> KEY_INHERITABLE_THREAD_LOCAL = new InheritableThreadLocal<>();

    /**
     * Redis Template
     */
    private static StringRedisTemplate orderLockRedisTemplate;
    /**
     * keySerializer
     */
    private static RedisSerializer<String> keySerializer;

    /**
     * valueSerializer
     */
    private static RedisSerializer<String> valueSerializer;

    /**
     * 在构造函数自动注入
     *
     * @param orderLockRedisTemplate redis对象
     */
    @SuppressWarnings("unchecked")
    @Autowired
    private OrderLockHelper(@Qualifier("orderLockRedisTemplate") final StringRedisTemplate orderLockRedisTemplate) {
        OrderLockHelper.orderLockRedisTemplate = orderLockRedisTemplate;
        OrderLockHelper.keySerializer = (RedisSerializer<String>) orderLockRedisTemplate.getKeySerializer();
        OrderLockHelper.valueSerializer = (RedisSerializer<String>) orderLockRedisTemplate.getValueSerializer();
    }

    /**
     * 尝试当前线程独占订单Id关联操作
     *
     * @param orderId    订单Id
     * @param lockReason 独占锁的原因
     * @return
     */
    public static boolean tryLock(final String orderId, final String lockReason) {
        return tryLock(orderId, lockReason, DEFAULT_TIMEOUT_SECONDS);
    }

    /**
     * 尝试当前线程独占订单Id关联操作,并限定超时时间
     *
     * @param orderId        订单Id
     * @param lockReason     独占锁的原因
     * @param timeoutSeconds 单位为秒
     * @return
     */
    public static boolean tryLock(final String orderId, final String lockReason, final int timeoutSeconds) {
        String lockKey = RedisKeyUtils.buildKey(RESOURCE_ID_PREFIX, orderId);
        byte[] rawKey = keySerializer.serialize(lockKey);
        byte[] rawValue = valueSerializer.serialize(lockReason);
        Boolean result = orderLockRedisTemplate.execute((RedisCallback<Boolean>) connection ->
                connection.set(rawKey, rawValue, Expiration.from(timeoutSeconds, TimeUnit.SECONDS), RedisStringCommands.SetOption.SET_IF_ABSENT)
        );
        if (result != null && result) {
            KEY_INHERITABLE_THREAD_LOCAL.set(lockKey);
            return true;
        }
        return false;
    }

    /**
     * 解除当前线程独占订单Id关联操作
     * XXX: 非线程拥有的锁unlock无效
     *
     * @return
     */
    public static void unlock() {
        String lockKey = KEY_INHERITABLE_THREAD_LOCAL.get();
        if (lockKey != null) {
            Boolean result = orderLockRedisTemplate.delete(lockKey);
            if (result != null && !result) {
                logger.error("解锁超时: lockKey={},请注意控制加锁区域内代码!!!", lockKey);
            }
            KEY_INHERITABLE_THREAD_LOCAL.remove();
        }
    }
}
0

评论区