Redis分布式锁

Wesley13
• 阅读 665

package com.sqi;

import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.params.SetParams;

import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeoutException;

/** * @author 麒 * @version 1.0 * @since 2019-03-22 */ public class RedisLockUtil { private static final String LOCK_SUCCESS = "OK"; private JedisPool jedisPool; private final Map<String, Boolean> DAEMON_MAP = new ConcurrentHashMap<String, Boolean>(); private static final String REENTRANT_LOCK_SCRIPT = "local value = redis.call('get',KEYS[1]) " + "if value then " + " if value == ARGV[1] then " + " return redis.call('pexpire', KEYS[1], ARGV[2]) " + " else " + " return 0 " + " end " + "else " + " redis.call('set',KEYS[1],ARGV[1],'NX','PX',ARGV[2]) " + " return 1 " + "end"; private static final String WAIT_LOCK_SCRIPT = "local value = redis.call('get',KEYS[1]) " + "if value then " + " if value == ARGV[1] then " + " return redis.call('pexpire', KEYS[1], ARGV[2]) " + " else " + " return redis.call('pttl', KEYS[1]) " + " end " + "else " + " redis.call('set',KEYS[1],ARGV[1],'NX','PX',ARGV[2]) " + " return 1 " + "end"; private static final String REFRESH_LOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then " + " return redis.call('pexpire', KEYS[1],ARGV[2]) " + "else " + " return 0 " + "end"; private static final String RELEASE_LOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then " + " return redis.call('del', KEYS[1]) " + "else " + " return 0 " + "end";

public RedisLockUtil(JedisPool jedisPool) {
    this.jedisPool = jedisPool;
}

protected Jedis getJedis() {
    return jedisPool.getResource();
}

/\*\*
 \* 获取redis分布式锁,只要锁存在就返回false
 \*
 \* @param lockKey   锁的名字
 \* @param lockOwner 锁的所有者,通常为当前请求的requestID
 \* @param expire    锁过期时间单位毫秒
 \* @return
 \*/
public boolean getSimpleRedisLock(String lockKey, String lockOwner, Long expire) {
    SetParams setParams = SetParams.setParams().nx().px(expire);
    String result = getJedis().set(lockKey, lockOwner, setParams);
    return result != null ? LOCK\_SUCCESS.equals(result) : false;
}

/\*\*
 \* 获取redis分布式锁, 如果锁存在且属于当前申请者,则刷新锁过期时间,返回true
 \*
 \* @param lockKey   锁的名字
 \* @param lockOwner 锁的所有者,通常为当前请求的requestID
 \* @param expire    锁过期时间单位毫秒
 \* @return
 \*/
public boolean getReentrantRedisLock(String lockKey, String lockOwner, Long expire) {
    List keyList = Collections.singletonList(lockKey);
    List argvList = Arrays.asList(lockOwner,expire.toString());
    Long result = (Long) getJedis().eval(REENTRANT\_LOCK\_SCRIPT, keyList, argvList);
    return result > 0;
}


/\*\*
 \* 获取Redis锁,如果锁被占用则阻塞
 \*
 \* @param lockKey   锁的名字
 \* @param lockOwner 锁的所有者,通常为当前请求的requestID
 \* @param expire    锁过期时间单位毫秒
 \* @param timeOut   超时时间毫秒
 \* @return
 \* @throws InterruptedException
 \* @throws TimeoutException
 \*/
public boolean waitRedisLock(String lockKey, String lockOwner, Long expire, Long timeOut) throws InterruptedException, TimeoutException {
    long startTime = System.currentTimeMillis();
    List keyList = Collections.singletonList(lockKey);
    List argvList = Arrays.asList(lockOwner,expire.toString());
    while (true) {
        long nowTime = System.currentTimeMillis();
        if (timeOut > 0 && (nowTime - startTime) >= timeOut) {
            throw new TimeoutException();
        }
        try {
            Long result = (Long) getJedis().eval(WAIT\_LOCK\_SCRIPT, keyList, argvList);
            if (result == 1) {
                return true;
            }
            Thread.sleep(result);
        } catch (Exception e) {
            Thread.sleep((timeOut >> 8) + 10);
        }
    }
}

/\*\*
 \* 刷新锁过期时间
 \*
 \* @param lockKey
 \* @param lockOwner
 \* @param expire
 \*/
public boolean refreshLockExpire(String lockKey, String lockOwner, Long expire) {
    List keyList = Collections.singletonList(lockKey);
    List argvList = Arrays.asList(lockOwner,expire.toString());
    return (Long) getJedis().eval(REFRESH\_LOCK\_SCRIPT, keyList, argvList) > 0;
}

/\*\*
 \* 释放锁
 \*
 \* @param lockKey
 \* @param lockOwner
 \* @return
 \*/
public boolean releaseLock(String lockKey, String lockOwner) {
    Long result = (Long) getJedis().eval(RELEASE\_LOCK\_SCRIPT, Collections.singletonList(lockKey), Collections.singletonList(lockOwner));
    if (result > 0) {
        DAEMON\_MAP.remove(lockKey);
        return true;
    }
    return false;
}

/\*\*
 \* 获取Redis锁,如果锁被占用则阻塞,启动一个守护线程用来保证在任务进行中锁不过期
 \*
 \* @param lockKey   锁的名字
 \* @param lockOwner 锁的所有者,通常为当前请求的requestID
 \* @param expire    锁过期时间单位毫秒
 \* @return
 \*/
public boolean waitRedisLockWithDaemon(final String lockKey, final String lockOwner, final Long expire, final long timeOut) throws TimeoutException, InterruptedException {
    boolean sig = waitRedisLock(lockKey, lockOwner, expire, timeOut);
    if (sig) {
        DAEMON\_MAP.put(lockKey, true);
        lockDaemon(lockKey, lockOwner, expire);
    }
    return sig;
}

/\*\*
 \* 获取redis分布式锁,启动一个守护线程用来保证在任务进行中锁不过期
 \*
 \* @param lockKey   锁的名字
 \* @param lockOwner 锁的所有者,通常为当前请求的requestID
 \* @param expire    锁过期时间单位毫秒
 \* @return
 \*/
public boolean getRedisLockWithDaemon(final String lockKey, final String lockOwner, final Long expire) {
    boolean sig = getSimpleRedisLock(lockKey, lockOwner, expire);
    if (sig) {
        DAEMON\_MAP.put(lockKey, true);
        lockDaemon(lockKey, lockOwner, expire);
    }
    return sig;
}

/\*\*
 \* 锁的守护线程,保证在锁持有期内不会过期
 \*
 \* @param lockKey
 \* @param lockOwner
 \* @param expire
 \*/
protected void lockDaemon(final String lockKey, final String lockOwner, final Long expire) {
    long exp;
    if (expire > 1000) {
        exp = expire >> 1;
    } else {
        exp = expire;
    }
    final long finalExp = exp;
    Thread thread = new Thread(new Runnable() {
        public void run() {
            Boolean sig = DAEMON\_MAP.get(lockKey);
            if (sig == null) {
                sig = false;
            }
            try {
                while (sig) {
                    if (refreshLockExpire(lockKey, lockOwner, expire)) {
                        Thread.sleep(finalExp);
                        sig = DAEMON\_MAP.get(lockKey);
                    } else {
                        sig = false;
                        DAEMON\_MAP.remove(lockKey);
                    }
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    });
    thread.setDaemon(true);
    thread.start();
}

}

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Easter79 Easter79
3年前
Twitter的分布式自增ID算法snowflake (Java版)
概述分布式系统中,有一些需要使用全局唯一ID的场景,这种时候为了防止ID冲突可以使用36位的UUID,但是UUID有一些缺点,首先他相对比较长,另外UUID一般是无序的。有些时候我们希望能使用一种简单一些的ID,并且希望ID能够按照时间有序生成。而twitter的snowflake解决了这种需求,最初Twitter把存储系统从MySQL迁移
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这