热门IT资讯网

单实例redis分布式锁的简单实现

发表于:2024-11-27 作者:热门IT资讯网编辑
编辑最后更新 2024年11月27日,redis分布式锁的基本功能包括, 同一刻只能有一个人占有锁, 当锁被其他人占用时, 获取者可以等待他人释放锁, 此外锁本身必须能超时自动释放.直接上java代码, 如下:package com.te

redis分布式锁的基本功能包括, 同一刻只能有一个人占有锁, 当锁被其他人占用时, 获取者可以等待他人释放锁, 此外锁本身必须能超时自动释放.

直接上java代码, 如下:

package com.test;import org.apache.commons.pool2.impl.GenericObjectPoolConfig;import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisPool;import java.util.Arrays;import java.util.concurrent.TimeUnit;/** * 简单的单实例redis分布式锁 * 没有实现的高级功能:锁的重入、锁的续约等 * * @Author:tt * @Description: * @CreateTime:2019/6/12 */public class SingleRedisLock {    private JedisPool jedisPool;    /**     * 获取锁     *     * @param lockKey          锁的key     * @param lockVal          锁的val,可以利用来实现"避免误删别人锁"、"锁的重入"等功能     * @param lockMaxLifeTime  锁的最大生命时长,到期自动销毁,单位:毫秒     * @param tryWaitingTime   等待获取锁的超时时间,单位:毫秒     * @param waitingSleepTime 等待获取锁的阻塞周期,单位:毫秒,设置过短会造成cpu竞争,设置过长会造成浪费,需依赖于'具体业务平均的执行时长'     * @return     */    public Boolean tryLock(String lockKey, String lockVal, int lockMaxLifeTime, int tryWaitingTime, int waitingSleepTime) {        //lua脚本,让逻辑简单清晰,同时保证原子性        //setNX:成功-1,失败-0        String lua = " if redis.call('set',KEYS[1],ARGV[1],'PX',ARGV[2],'NX') then return 1 else return 0 end ";        //获取锁的开始时间        Long tryBeginTime = System.currentTimeMillis();        //轮询        while (true) {            Long result = null;            Jedis jedis = null;            try {                jedis = jedisPool.getResource();                result = (Long) jedis.eval(lua, Arrays.asList(lockKey), Arrays.asList(lockVal, String.valueOf(lockMaxLifeTime)));            } catch (Exception e) {                e.printStackTrace();            } finally {                if (jedis != null) {                    try {                        jedis.close();                    } catch (Exception e) {                    }                }            }            //获取锁成功            if (Long.valueOf(1).equals(result)) {                return true;            }            //当前时间            Long now = System.currentTimeMillis();            //获取等待超时,就不用获取了            if (now - tryBeginTime >= tryWaitingTime) {                return false;            }            try {                //阻塞等一会儿再重新去获取                TimeUnit.MILLISECONDS.sleep(waitingSleepTime);            } catch (InterruptedException e) {            }        }    }    /**     * 释放锁     *     * @param lockKey     * @param lockVal     * @return     */    public void releaseLock(String lockKey, String lockVal) {        //如果lockVal是自己的再删除,防止误删,场景来源:当前锁的持有者操作时间太长,锁已经自动释放并被别人占有了        String lua = "if redis.call('get', KEYS[1]) == ARGV[1] then redis.call('del', KEYS[1]) end ";        Jedis jedis = null;        try {            jedis = jedisPool.getResource();            jedis.eval(lua, Arrays.asList(lockKey), Arrays.asList(lockVal));        } catch (Exception e) {            e.printStackTrace();        } finally {            if (jedis != null) {                try {                    jedis.close();                } catch (Exception e) {                }            }        }    }    //测试    public static void main(String[] args) {        //连接池        JedisPool jedisPool = new JedisPool(new GenericObjectPoolConfig(), "127.0.0.1", 6379, 2000, "test123");        SingleRedisLock simpleRedisLock = new SingleRedisLock();        simpleRedisLock.jedisPool = jedisPool;        //模拟10个并发        for (int i = 0; i < 10; i++) {            new Thread(() -> {                String lockKey = "TEST_LOCK_KEY";                String threadName = Thread.currentThread().getName();                //获取锁                Boolean locked = simpleRedisLock.tryLock(lockKey, threadName,                        30000, 5000, 200);                //获取锁失败                if (!locked) {                    System.err.println(">>> " + threadName + " 获取锁失败");                    return;                }                //获取锁成功,模拟执行业务操作                System.out.println(">>> " + threadName + " 获取锁成功");                doShortBusiness();                //doLongBusiness();                //释放锁                simpleRedisLock.releaseLock(lockKey, threadName);            }).start();        }        try {            TimeUnit.MILLISECONDS.sleep(60000);        } catch (InterruptedException e) {        }    }    //短任务:100毫秒    static void doShortBusiness() {        try {            TimeUnit.MILLISECONDS.sleep(100);        } catch (InterruptedException e) {        }    }    //长任务:3秒    static void doLongBusiness() {        try {            TimeUnit.MILLISECONDS.sleep(3000);        } catch (InterruptedException e) {        }    }}

锁的高级功能包含锁的重入、锁的续约等, 当然为了保证锁的高可用, redis还有主从、集群等部署方式, 对应的锁的实现也有区别, 略微复杂, 不过有现成的框架可供我们参考使用, 比较知名的如Redisson, 一个强大的redis客户端, 当然包括对"分布式锁"的完美实现, 其支持redis单实例、哨兵、集群等模式。

写在最后

0