一、为何Redis能够轻易地完成分布式锁
1、Redis为单历程单线程形式,采纳行列形式将并发接见变成串行接见,且多客户端对Redis的衔接并不存在合作关联。
2、Redis的SETNX敕令能够轻易的完成分布式锁。
setNX(SET if Not eXists)
语法:SETNX key value
返回值:设置胜利,返回 1 ;设置失利,返回 0 。
当且仅当 key 不存在时将 key 的值设为 value,并返回1;若给定的 key 已存在,则 SETNX 不做任何行动,并返回0。
综上所述,能够经由历程setnx的返回值来推断是不是猎取到锁,而且不必忧郁并发接见的题目,由于Redis是单线程的,所以假如返回1则猎取到锁,返回0则没猎取到。当营业操纵实行完后,一定要开释锁,开释锁的逻辑很简单,就是把之前设置的key删撤除即可,如许下次又能够经由历程setnx该key猎取到锁了。
二、分布式锁完成
我们已晓得能够经由历程Redis自带的函数setNX来完成分布式锁,细致完成步骤以下。
我在一台CentOS7的linux虚拟机中安装了Redis效劳,ip地点为:192.168.246.130,效劳端口为:6379。
下面是java经由历程redis完成分布式锁的例子:
import redis.clients.jedis.Jedis; public class RedisLock { //锁的key private static final String key = "DistributedRedisLock"; private static Integer count = 0; public static void main(String[] args) { for(int i=0;i<1000;i++){ new Thread(new Runnable() { @Override public void run() { //猎取Redis衔接 Jedis jedis = new Jedis("192.168.246.130", 6379); try{ while(true){ //猎取锁 if(jedis.setnx(key, Thread.currentThread().getName()) == 1){ try{ System.out.println("线程("+Thread.currentThread().getName()+")猎取到锁,最先实行操纵"); count++; System.out.println(count); break; }finally{ System.out.println("操纵实行完成,开释锁"); //操纵实行完一定要开释锁,所以在finally块中实行 jedis.del(key); } }else{ //返回的不是1,申明已有某个线程猎取到了锁 try { //守候100毫秒以后重试 Thread.sleep(100l); } catch (InterruptedException e) { e.printStackTrace(); } } } }catch(Exception e){ e.printStackTrace(); }finally{ //开释Redis衔接 jedis.disconnect(); } } }).start(); } } }
上述代码的输出效果为:
线程(Thread-320)猎取到锁,最先实行操纵 1 操纵实行完成,开释锁 线程(Thread-463)猎取到锁,最先实行操纵 2 操纵实行完成,开释锁 线程(Thread-997)猎取到锁,最先实行操纵 3 操纵实行完成,开释锁 ... 线程(Thread-409)猎取到锁,最先实行操纵 998 操纵实行完成,开释锁 线程(Thread-742)猎取到锁,最先实行操纵 999 操纵实行完成,开释锁 线程(Thread-286)猎取到锁,最先实行操纵 1000 操纵实行完成,开释锁
上述代码虽然是在单运用多线程情况下测试的,但即使是在分布式环境下多运用多线程去猎取锁,效果依然是准确的。
三、处置惩罚死锁题目
之前的例子代码只是测试代码,只是为了申明道理,例子自身很简单,所以有一些考虑不周的处所。比方当猎取到锁以后在营业操纵实行历程中发生了环境题目致使断开了和Redis的衔接,那就没法在finally块中开释锁,致使其他守候猎取锁的线程无穷守候下去,也就是发生了死锁征象。
处置惩罚体式格局:
能够在Redis中给锁设置一个逾期时候,如许即使没法开释锁,锁也能在一段时候后自动开释。
代码上只需要在猎取到锁以后在try语句块中到场以下代码:
jedis.expire(key, 10); //这里给锁设置10秒的逾期时候
更妥帖的处置惩罚体式格局:
第一个处置惩罚体式格局并非很好,由于当营业操纵处置惩罚时候很长,超过了设置的逾期时候,那锁就自动开释了,然后再实行finally块中开释锁的操纵时,这个锁能够已被其他线程所持有,会致使把其他线程持有的锁给开释了,从而致使并发题目。所以更妥帖一点的体式格局是在开释锁时推断一下锁是不是已逾期,假如已逾期就不必再开释了。
代码上把猎取到锁以后的操纵改成以下代码:
long start = System.currentTimeMillis(); //猎取肇端时候毫秒数 try{ jedis.expire(key, 10); ... }finally{ ... if(System.currentTimeMillis() < start+10*1000){ //假如之前设置的锁还未逾期,则开释掉 jedis.del(key); } }
以上就是Java基于Redis完成分布式锁的细致内容,更多请关注ki4网别的相干文章!