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

栏目: 数据库 · 发布时间: 6年前

内容简介:redis分布式锁的基本功能包括, 同一刻只能有一个人占有锁, 当锁被其他人占用时, 获取者可以等待他人释放锁, 此外锁本身必须能超时自动释放.直接上java代码, 如下:

单实例 <a href='https://www.codercto.com/topics/18994.html'>redis</a> 分布式锁的简单实现

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单实例、哨兵、集群等模式。

写在最后

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


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Cascading Style Sheets 2.0 Programmer's Reference

Cascading Style Sheets 2.0 Programmer's Reference

Eric A. Meyer / McGraw-Hill Osborne Media / 2001-03-20 / USD 19.99

The most authoritative quick reference available for CSS programmers. This handy resource gives you programming essentials at your fingertips, including all the new tags and features in CSS 2.0. You'l......一起来看看 《Cascading Style Sheets 2.0 Programmer's Reference》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

URL 编码/解码
URL 编码/解码

URL 编码/解码