使用redis实现分布式锁基本就2步:第一多线程请求处理获取锁时,使用setnx指令,只有当key不存在时才设值,注意设置超时时间。

set key value [expiration EX seconds|PX milliseconds] [NX|XX]

第二相关业务处理完成之后,谁加的锁应该由谁去及时释放,即删除指定的key,注意删除时要匹配加锁线程的value,避免因超时误删其它线程的锁,这里使用Lua脚本执行原子操作,也可以先判断key value再del。

if redis.call("get",KEYS[1]) == ARGV[1] then
 return redis.call("del",KEYS[1])
else
 return 0
end

----

下面使用单节点redis,redis版本5.0.8,版本客户端使用jedis3.2.0,完成相关加锁,释放锁的操作。

<dependency>
   <
groupId>redis.clients</groupId>
   <
artifactId>jedis</artifactId>
   <
version>3.2.0</version>
</
dependency>

注意使用jedisPool时默认最大连接数是8,使用完记得关闭连接,否则会造成线程等待,set成功时返回"OK",lua脚本删除成功时,返回"1"。

public boolean lock(String key, String value, long waitTimeOut, SetParams params) {
   
Jedis jedis = null;

   try {

    jedis = JedisPool.getResource();
       long

       long start = System.currentTimeMillis();
       for
(; ; ) {
           
String lock = jedis.set(key, value, params);
           if
("OK".equals(lock)) {
               return true;
           
}
           
long wait = System.currentTimeMillis() - start;
           if
(wait >= waitTimeOut) {
               return false;
           
}
           
try {
               
Thread.sleep(100);
           
} catch (InterruptedException e) {
               log
.error("");
               return false;
           
}
       }
   }
catch (Exception e) {
       
e.printStackTrace();
       return false;
   
} finally {
       
jedis.close();
   
}
}
public boolean unlock(String key, String value) {
   
Jedis jedis = null;
   try
{
       
jedis = JedisPool.getResource();
       
String script = "if redis.call('get',KEYS[1]) == ARGV[1] " +
               "then return redis.call('del',KEYS[1]) " +
               "else return 0 end";
       
Object result = jedis.eval(script, Collections.singletonList(key),
               
Collections.singletonList(value));
       if
("1".equals(result.toString())) {
           
return true;
       
}
       
return false;
   
} catch (Exception e) {
       
e.printStackTrace();
       return false;
   
} finally {
       
jedis.close();
   
}
}

----

最后,在controller里简单测试一下锁的效果:

@GetMapping("hello")
public String hello() {
   
String value = System.nanoTime() + "." + UUID.randomUUID();
   
CountDownLatch latch = new CountDownLatch(1000);
   for
(int i = 0; i < 1000; i++) {
       
new Thread(() -> {
           
redisLock.lock(KEY,value,1000,SetParams.setParams().nx().px(500000));
           
amount--;
           
System.out.println(Thread.currentThread() + ":" + amount);
           
redisLock.unlock(KEY,value);
           
latch.countDown();
       
}).start();
   
}
   
try {
       
latch.await();
   
} catch (InterruptedException e) {
       
e.printStackTrace();
   
}
   
return "amount:" + amount;
}

不加锁时多次执行会出现数据错误,添加锁后数据正常,可以使用专业的工具进行测试验证,这里为了等待所有线程执行完成使用了countDownLatch。

----

这里的分布式锁是基于redis单点完成的,使用单机可能会出现单节点故障。且锁是不可重入的。通常redis都是集群部署的,可以使用redission来完成集群redis的分布式锁操作,实现了redlock算法,且value关联线程,实现了锁的可重入。


©著作权归作者所有:来自51CTO博客作者mb5fdb0a4002420的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. 基于数据库实现分布式锁
  2. 从GC的SuppressFinalize方法带你深刻认识Finalize底层运行机制
  3. 不要把异常当做业务逻辑,这性能可能你无法承受
  4. 同步异步多线程这三者关系,你能给面试官一个满意的回答吗?
  5. 使用docker-compose 一键部署你的分布式调用链跟踪框架skywalkin
  6. 多线程基础知识
  7. 【金三银四】Java并发编程面试题(2021最新版)
  8. 怎么给女朋友讲明白线程池?
  9. Java程序员除了「北上广深」其它地方能拿到30K吗?

随机推荐

  1. Android RadioGroup和RadioButton使用
  2. 【Android Training视频系列】第4讲Build
  3. Android所有系统资源图标android.R.drawa
  4. Intent实现Android间的页面跳转
  5. android 开发BUG
  6. Android中事件分发机制分析
  7. Android下基于XML的Graphics shape使用方
  8. 【Android学习入门】Android studio基本
  9. dev android project from cmd
  10. Android google地图开发的前期准备(MD5和