自己写了个简单的redis分布式锁

【注意:此锁需要在每次使用前都创建对象,也就是要在线程内每次都创建对象后使用】

package redis;

import java.util.Collections;
import java.util.Random;
import java.util.UUID; import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config; import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool; public class RedisLock2 { JedisPool jedisPool;
private String key;
private String value;
//默认锁超时时间5秒
private Long timeout = 5L;
//加锁成功的时间起点
private Long startTime; /**
* 使用默认超时时间
* @param JedisPool
* @param key
*/
public RedisLock2(JedisPool JedisPool,String key) {
super();
this.jedisPool = JedisPool;
this.key = key;
value = System.currentTimeMillis()+"";
} /**
* 单独设置超时时间
* @param JedisPool
* @param key 锁的名称
* @param timeout 锁超时时间
*/
public RedisLock2(JedisPool JedisPool,String key,Long timeout) {
super();
this.jedisPool = JedisPool;
this.key = key;
this.timeout = timeout;
value = UUID.randomUUID().toString();
// System.out.println("创建锁时的value:"+value);
} /**
* 单次加锁,需要判断返回值【适用于获取不到锁就返回的业务场景】
* @return true:加锁成功; false:加锁失败
*/
public boolean lock() {
Jedis jedis = jedisPool.getResource();
boolean ok = jedis.set(key,value, "nx", "ex", timeout)!=null;
jedis.close();
if (!ok) {
//加锁失败
return false;
}
//加锁成功
startTime = System.currentTimeMillis();
return true;
} /**
* 阻塞重试加锁,默认时间间隔10毫秒
*/
public void lockRetry() {
lockRetry(null);
} /**
* 阻塞重试加锁,直到加锁成功【适用于一定要执行的业务】
* @param interval 重试时间间隔毫秒值
*/
public void lockRetry(Long interval) {
if (interval==null) {
//默认10毫秒重试一次
interval = 10L;
}else if(interval==0) {
//如果传值为0,则取10以内随机值
interval = (long) (new Random().nextInt(10) + 1);
}
int num = 0;
while (true) {
boolean b = lock();
num++;
if (b) {
System.out.println("加锁 "+num+" 次后成功");
break;
}
try {
//休息10毫秒后重试
Thread.sleep(interval);
System.out.println("有并发,休息:"+interval+"毫秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} /**
* 解锁【首先尝试用lua方式,如果redis的版本不支持lua,用普通方式】
* @return
*/
public String unlock() {
//解锁返回消息(非正常解锁时会打印,还可以在业务中解锁时判断此返回值)
String msg = "";
//加锁到解锁消耗时间
long timeConsume = System.currentTimeMillis() - startTime;
//超时直接返回
if (timeConsume > timeout * 1000) {
System.out.println("出现超时解锁-----------key:" + key + ",耗时毫秒:" + timeConsume);
// return false;
msg = "出现超时解锁--key:" + key + ",耗时毫秒:" + timeConsume;
return msg;
}
//这里是为了避免超时后,释放掉其他线程的同名锁
String luaScript = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else return 0 end";
Jedis jedis = jedisPool.getResource();
//执行lua脚本返回值
Object evalRtn = 0;
try {
evalRtn = jedis.eval(luaScript, Collections.singletonList(key), Collections.singletonList(value));
// System.out.println("lua解锁返回值:"+evalRtn);
} catch (Exception e) {
e.printStackTrace();
//如果当前redis不支持lua脚本,用下面方法,但是下面的代码不是原子操作,可能会有并发问题,这里忽略
String s = jedis.get(key);
if (value.equals(s)) {
//释放锁
jedis.del(key);
// return true;
msg = "解锁成功";
return msg;
}
}finally {
jedis.close();
}
if ((Long) evalRtn == 1) {
// return true;
msg = "解锁成功";
return msg;
}
System.out.println("出现其他异常解锁失败---------key:" + key);
// return false;
msg = "出现其他异常解锁失败--key:" + key;
return msg;
} //简单加锁测试
public static void main1(String[] args) throws InterruptedException {
JedisPool jedisPool = new JedisPool("127.0.0.1", 6379);
//设置超时时间100秒
RedisLock2 redisLock = new RedisLock2(jedisPool,"lock1",100L);
//加锁
boolean lock = redisLock.lock();
//获取到锁才执行,获取不到不执行
if(lock) {
//执行业务逻辑(要保证业务逻辑执行时间小于锁超时时间)
System.out.println("我获取到锁了");
// Thread.sleep(2000);
}
System.out.println(redisLock.unlock());
} //开启多线程往redis中设置值,保证不覆盖(用单次锁在业务逻辑中循环阻塞)
public static void main2(String[] args) {
JedisPool jedisPool = new JedisPool("127.0.0.1", 6379);
//原来是使用网上的一种锁
// DistributedLock lock = new DistributedLock(jedisPool); for (int i = 0; i < 3; i++) {
// final int k = i;
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 10; j++) {
int k = j;
// 连接本地的 Redis 服务
// Jedis jedis = new Jedis("localhost");
// String code = null;
RedisLock2 myLock = null;
try {
//加分布式锁
// code = lock.lock("mylock");
myLock = new RedisLock2(jedisPool,"lock1",10L);
while (true) {
//不断获取锁
boolean lock = myLock.lock();
if (lock) {
//如果获取到则执行
// 从连接池中获取一个jedis对象
Jedis jedis = jedisPool.getResource();
if (!jedis.exists("a" + k)) {
jedis.set("a" + k, Thread.currentThread().getName());
jedis.expire("a" + k, 60);
System.out.println(System.currentTimeMillis() + "--" + Thread.currentThread().getName()
+ "--key:" + ("a" + k) + "不存在,设置值为: " + Thread.currentThread().getName());
try {
// Thread.sleep((long) (Math.random()*1000));
} catch (Exception e) {
e.printStackTrace();
}
} else {
System.out.println(Thread.currentThread().getName() + "--key:" + ("a" + k) + "存在,值为: "
+ jedis.get("a" + k));
}
jedis.close();
break;//跳出循环
} } } finally {
//释放分布式锁
// lock.unLock("mylock",code);
//执行完解锁
myLock.unlock();
}
}
}
}).start();
}
} //开启多线程往redis中设置值,保证不覆盖(用阻塞锁)
public static void main(String[] args) {
//原来是使用rdissen锁
//获取redisson
// Config config = new Config();
// config.useSingleServer().setAddress("redis://localhost:6379");
// RedissonClient redisson = Redisson.create(config);
// //获取锁
// RLock lock = redisson.getLock("mylock"); // 创建连接池对象
JedisPool jedisPool = new JedisPool("127.0.0.1", 6379); for (int i = 0; i < 3; i++) {
// final int k = i;
new Thread(new Runnable() {
@Override
public void run() {
for (int j = 0; j < 10; j++) {
int k = j;
// 连接本地的 Redis 服务
// Jedis jedis = new Jedis("localhost");
RedisLock2 myLock = null;
try {
//加redisson分布式锁
// lock.lock();
//用我们自己写的重试锁【自定义锁非可重入锁,需要在线程中每次使用时都创建一个锁对象,多线程中只要名称相同就认为是同一个锁】
myLock = new RedisLock2(jedisPool,"lock1",10L);
//开启阻塞锁
myLock.lockRetry(3L);
//执行业务逻辑【因为用阻塞锁,无需判断返回值】
// 从连接池中获取一个jedis对象
Jedis jedis = jedisPool.getResource();
if (!jedis.exists("a" + k)) {
jedis.set("a" + k, Thread.currentThread().getName());
jedis.expire("a" + k, 60);
// System.out.println(System.currentTimeMillis() + "--" + Thread.currentThread().getName()
// + "--key:" + ("a" + k) + "不存在,设置值为: " + Thread.currentThread().getName());
try {
// Thread.sleep((long) (Math.random()*1000));
} catch (Exception e) {
e.printStackTrace();
}
} else {
// System.out.println(Thread.currentThread().getName() + "--key:" + ("a" + k) + "存在,值为: "
// + jedis.get("a" + k));
}
jedis.close();
} finally {
//释放redisson分布式锁
// lock.unlock();
//用我自己定义的锁
myLock.unlock();
}
}
}
}).start();
} } }

自己写了个简单的redis分布式锁【我】的更多相关文章

  1. java架构之路-(Redis专题)简单聊聊redis分布式锁

    这次我们来简单说说分布式锁,我记得过去我也过一篇JMM的内存一致性算法,就是说拿到锁的可以继续操作,没拿到的自旋等待. 思路与场景 我们在Zookeeper中提到过分布式锁,这里我们先用redis实现 ...

  2. redis分布式锁练习【我】

    package redis; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; public class ...

  3. Redis分布式锁实现简单秒杀功能

    这版秒杀只是解决瞬间访问过高服务器压力过大,请求速度变慢,大大消耗服务器性能的问题. 主要就是在高并发秒杀的场景下,很多人访问时并没有拿到锁,所以直接跳过了.这样就处理了多线程并发问题的同时也保证了服 ...

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

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

  5. redis 分布式锁的简单使用

    RedisLock--让 Redis 分布式锁变得简单 目录 1. 项目介绍 2. 快速使用 2.1 引入 maven 坐标 2.2 注册 RedisLock 2.3 使用 3. 参与贡献 4. 联系 ...

  6. 利用redis分布式锁的功能来实现定时器的分布式

    文章来源于我的 iteye blog http://ak478288.iteye.com/blog/1898190 以前为部门内部开发过一个定时器程序,这个定时器很简单,就是配置quartz,来实现定 ...

  7. redis分布式锁和消息队列

    最近博主在看redis的时候发现了两种redis使用方式,与之前redis作为缓存不同,利用的是redis可设置key的有效时间和redis的BRPOP命令. 分布式锁 由于目前一些编程语言,如PHP ...

  8. redis咋么实现分布式锁,redis分布式锁的实现方式,redis做分布式锁 积极正义的少年

    前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介 ...

  9. Redis分布式锁的正确实现方式

    前言 分布式锁一般有三种实现方式:1. 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁.本篇博客将介绍第二种方式,基于Redis实现分布式锁.虽然网上已经有各种介 ...

随机推荐

  1. oracle排序怎样弄成1 2 3 ,而不是 1 10 100

    oracle表字段设置得值不是number,而是Varchar2时排序就会出现这种问题 这个时候排序的时候需要转类型排序: order by to_number(顺序号) asc

  2. Java发送email的端口问题

    Could not connect to SMTP host: smtp.***.com, port: 465, response: -1 使用Java发送email 的端口问题.一般使用25端口即可 ...

  3. BCB 编写服务程序的一个注意事项

      BCB编写服务,install报错的一个问题 今天编写了一个服务,最后INSTALL 的时候报错,如图: 经过近1小时的比较(俺过去写例子),居然无意中设置了一个属性               ...

  4. Java abstract类的基本使用 和 [abstract类实现]打印1000以内的所有素数并输出时间

    笔记: /** 关键字abstract ,实现抽象类,相当于给出类的大纲,子类只管继承,但抽象类不可被实例化! * 1.抽象方法只保留方法的功能,而具体的执行,交给继承抽象类的子类,由子类重写所有的抽 ...

  5. JS 截取地址栏指定字符后的内容

    一.获取地址栏路径 var url = window.location.href; 二.截取指定字符后的内容 /** * 截取指定字符后的内容 * @param url 路径 * @param par ...

  6. halcon导出类---HDevWindowStack详解

    在HDevelop中编写好的程序在导出时,Halcon会帮我们转换成我们需要的语言,比如C++.例:HDevelop中有如下语句需要导出: dev_close_window() Halcon导出成C+ ...

  7. vue,react,angular三大web前端流行框架简单对比

    常用的到的网站 vue学习库: https://github.com/vuejs/awesome-vue#carousel (json数据的格式化,提高本地测试的效率) json在线编辑: http: ...

  8. 2019HDU多校第五场A fraction —— 辗转相除法|类欧几里得

    题目 设 $ab^{-1} = x(mod \ p)$,给出 $x,p$,要求最小的 $b$,其中 $0< a < b, \ 1 < x<p,\ 3 \leq x\leq {1 ...

  9. X509

    1 打开iis 找到部署的站点应用连接池,高级设置,“加载用户配置文件”项的值改为true 2 用户:certmgr.msc 本地计算机:mmc——文件——证书 --CN = QALINE001.on ...

  10. KETTLE 更新表的两种方式-更新控件和sql更新 2种方式的实现比较

    在实际工作中,我们有可能遇见只更新不插入的情况,可以由以下2种方式去实现: 1.更新控件 如下图所示,根据id字段,更新name和cjsj时间字段 该控件不足的地方是,用来查询关键值得字段不够灵活,一 ...