java redisson 分布式锁,java使用redis实现分布式锁
分布式锁可以通过多种方式实现,如zookeeper、redis.无论哪种方式,他的基本原理都是一样的:锁用一个状态值来表示,锁的占用和释放用状态值来标识。
如何解决写爬虫IP受阻的问题?立即使用。
一、为什么Redis可以方便地实现分布式锁
1.Redis是单进程单线程模式,采用队列模式将并发访问改为串行访问,多个客户端之间不存在连接Redis的竞争。
2.Redis的SETNX命令可以方便地实现分布式锁。
setNX(不存在时设置)
语法:SETNX键值
返回值:设置成功,返回1;设置失败,返回0。
并仅在键不存在时将键的值设置为value,返回1;如果给定的键已经存在,SETNX不做任何事情并返回0。
综上所述,你可以通过setnx的返回值来判断锁是否被获取,不用担心并发访问。因为Redis是单线程的,如果返回1,就获取锁,如果返回0,就不获取锁。当业务操作完成时,锁必须被释放。释放锁的逻辑很简单,就是把之前设置的setnx键删除就可以了,这样下次设置nx这个键就可以获得锁了。
二、分布式锁实现
我们已经知道分布式锁可以通过Redis自己的函数setNX来实现。具体步骤如下。
我在CentOS7的一个linux虚拟机上安装了Redis服务,ip地址:192.168.246.130,服务端口:6379。
下面是一个java通过redis实现分布式锁的例子:
导入redis . clients . jedis . jedis;
公共类RedisLock {
//锁的钥匙
private static final String key= DistributedRedisLock ;
私有静态整数计数=0;
公共静态void main(String[] args) {
for(int I=0;i1000i ){
新线程(新Runnable() {
@覆盖
公共无效运行(){
//获取Redis连接
Jedis jedis=新Jedis(192.168.246.130 ,6379);
尝试{
while(true){
//获取锁
if(jedis.setnx(key,Thread.currentThread()。getName())==1){
尝试{
system . out . println( Thread( Thread . current Thread()。getName())获取锁并开始操作);
数数;
system . out . println(count);
打破;
}最后{
System.out.println(操作完成,释放锁);
//操作执行后必须释放锁,所以在finally块中执行。
jedis.del(键);
}
}否则{
//返回的不是1,表示某个线程已经获得了锁。
尝试{
//等待100毫秒,然后重试
thread . sleep(100 l);
} catch (InterruptedException e) {
e . printstacktrace();
}
}
}
}catch(异常e){
e . printstacktrace();
}最后{
//释放Redis连接
jedis . disconnect();
}
}
}).start();
}
}
}上面代码的输出结果是:
线程(Thread-320)获得锁并开始执行操作。
一个
操作完成,解除锁定。
线程(Thread-463)获得锁并开始执行操作。
2
操作完成,解除锁定。
线程(Thread-997)获得锁并开始执行操作。
三
操作完成,解除锁定。
.
线程(Thread-409)获得锁并开始执行操作。
998
操作完成,解除锁定。
线程(Thread-742)获得锁并开始执行操作。
紧急服务电话
操作完成,解除锁定。
线程(Thread-286)获得锁并开始执行操作。
1000
操作完成后,锁被释放。虽然上面的代码是在单个应用程序和多线程的条件下测试的,但是即使在分布式环境下使用多个应用程序和多线程来获取锁,结果仍然是正确的。
三、解决死锁问题
前面的示例代码只是一个测试代码,只是为了解释原理。例子本身很简单,所以有些欠考虑。例如,当一个业务操作在获得锁后执行过程中出现环境问题,与Redis的连接断开,锁无法在finally块中释放,导致其他等待锁的线程无限期等待,即发生死锁。
解决方式:
您可以在Redis中为锁设置一个到期时间,这样即使锁不能被释放,也可以在一段时间后自动释放锁。
代码只需要在获取锁后在try语句块中添加以下代码:
jedis.expire(key,10);//在这里,为锁更妥善的解决方式:设置10秒的到期时间。
第一种解决方案不太好,因为当业务操作处理时间较长,超过设定的到期时间时,锁会自动解除。然后,在finally块中执行释放锁的操作时,锁可能已经被其他线程持有,这将导致其他线程持有的锁被释放,从而产生并发问题。所以更好的方法是在释放锁的时候判断锁是否过期。如果已经过期,就不用再放了。
代码上把获取到锁之后的操作改为如下代码:
long start=system . current time millis();//获取以毫秒为单位的开始时间
尝试{
jedis.expire(key,10);
.
}最后{
.
if(system . current time millis()start 10 * 1000){
//如果先前设置的锁尚未过期,则释放它
jedis.del(键);
}
}以上是Java基于Redis实现分布式锁的细节。更多请关注我们的其他相关文章!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。