基于redis用java实现分布式锁设计与实现
cookqq ›博客列表 ›redis

基于redis用java实现分布式锁设计与实现

2017-07-12 10:38:44.0|分类: redis|浏览量: 4030

摘要: 基于redis实现分布式锁,重点解决如下问题: (1)分布式锁必须要设置一个过期时间 (2)加锁和设置失效时间必须是原子操作 (3)线程加锁时候设置一个随机字符串 (4)释放锁其实包含get、判断、del三步操作,三步操作必须是原子性。

分布式锁应用场景比较多,百度一下很多,本文就略过了,直接描述实现过程



举个列子:电商搞秒杀活动,需要进行设置剩余数量,count必须是线程安全的!count暂时代表秒杀手机剩余数量!

QQ截图20170624174527.png

步骤1:线程A获取redis分布式锁,如果获取锁成功,继续执行步骤2,

步骤2:线程A执行业务操作,设置count值为100

步骤3:线程A释放掉redis的分布式锁

步骤4:线程B获取redis的分布式锁,发现锁没有被使用,线程B则获取锁成功

步骤5:线程B设置count值为99

步骤6:线程B释放掉redis的分布式锁


上面流程有如下问题:

(1)线程A成功获取锁后,突然线程A挂掉了,redis的分布式锁没有被释放,其他线程获取锁的时候发现锁被占用,锁不能获取成功。整个程序就挂掉了! 

    解决办法:给锁增加一个超时时间1s。超过1s后redis自动释放这个锁

(2)如果线程A执行业务操作,耗时2s,2s大于redis超时时间1s。在这期间redis自动释放锁,线程B成功获取了锁,并且将count设置成99。线程A执行完业务操作,设置count=100,这就出现了脏数据

    解决办法:线程加锁的时候设置一个随机数,释放锁的时候进行比较,这把锁是否是自己加的锁


基于redis实现分布式锁,重点解决如下问题:

(1)分布式锁必须要设置一个过期时间

(2)加锁和设置失效时间必须是原子操作

(3)线程加锁时候设置一个随机字符串

(4)释放锁其实包含get、判断、del三步操作,三步操作必须是原子性。


java实现基于redis实现分布式锁思想:

(1)redis定义一个key

(2)java获取锁

(3)java释放锁


代码实现:

public interface DistributedLock {


public boolean getLock();

public void releaseLock();

}


public class RedisDistributedLock implements DistributedLock {


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

public static final String LOCK_NODE ="LOCK";

ThreadLocal<String> threadLocal = new ThreadLocal<String>();

@Override

public boolean getLock() {

Jedis jedis = new Jedis("localhost");

String value = UUID.randomUUID().toString();

String ret = jedis.set(LOCK_NODE, value, "NX", "PX", 10000);

logger.warn("ret="+ret);

System.out.println("ret="+ret);

if(ret!=null && ret.equals("OK")){

threadLocal.set(value);

jedis.close();

return true;

}else{

jedis.close();

}

return false;

}


@Override

public void releaseLock() {

String script = readFileByLines("E:\\workhome\\workspaceMarsMvn\\redis\\src\\main\\resources\\unlock.lua");

System.err.println(script);

Jedis jedis = new Jedis("localhost");

List<String> keys = new ArrayList<String>();

keys.add(LOCK_NODE);

List<String> args = new ArrayList<String>();

args.add(threadLocal.get());

System.err.println("releaseLock" + keys.toString()+" | "+args.toString());

Object object = jedis.eval(script, keys, args);

System.err.println("releaseLock" + object);

jedis.close();

}

/**

     * 以行为单位读取文件,常用于读面向行的格式化文件

     */

    public static String readFileByLines(String fileName) {

        File file = new File(fileName);

        BufferedReader reader = null;

        StringBuffer content = new StringBuffer();

        try {

            reader = new BufferedReader(new FileReader(file));

            String tempString = null;

            // 一次读入一行,直到读入null为文件结束

            while ((tempString = reader.readLine()) != null) {

            content.append(tempString+"\n");

            }

            reader.close();

        } catch (IOException e) {

            e.printStackTrace();

        } finally {

            if (reader != null) {

                try {

                    reader.close();

                } catch (IOException e1) {

                }

            }

        }

        return content.toString();

    }



}


unlock.lua代码

if redis.call("get", KEYS[1]) == ARGV[1]

then

return redis.call("del", KEYS[1])

else

return 0

end




一键分享文章

分类列表

  • • struts源码分析
  • • flink
  • • struts
  • • redis
  • • kafka
  • • ubuntu
  • • zookeeper
  • • hadoop
  • • activiti
  • • linux
  • • 成长
  • • NIO
  • • 关键词提取
  • • mysql
  • • android studio
  • • zabbix
  • • 云计算
  • • mahout
  • • jmeter
  • • hive
  • • ActiveMQ
  • • lucene
  • • MongoDB
  • • netty
  • • flume
  • • 我遇到的问题
  • • GRUB
  • • nginx
  • • 大家好的文章
  • • android
  • • tomcat
  • • Python
  • • luke
  • • android源码编译
  • • 安全
  • • MPAndroidChart
  • • swing
  • • POI
  • • powerdesigner
  • • jquery
  • • html
  • • java
  • • eclipse
  • • shell
  • • jvm
  • • highcharts
  • • 设计模式
  • • 列式数据库
  • • spring cloud
  • • docker+node.js+zookeeper构建微服务
版权所有 cookqq 感谢访问 支持开源 京ICP备15030920号
CopyRight 2015-2018 cookqq.com All Right Reserved.