假设,对一个商品表的简单操作,一个线程去修改这个商品的信息,首先得从数据库中查出这条数据,然后加载在内存中,在内存修改完成之后,再存到数据库里面,对于单线程而言这一个完整的操作是没问题的,但是在多线程中,由于读取,修改,保存到数据库不是原子操作(原子特性:不可分割,要么全都成功,要么全都失败),所以这种操作出现在多线程中就会有很大的问题。
分布式锁的思路不难,其实就是先进来的线程先占位置,当其他线程进来时,发现位置被人占了,就会放弃争夺或者稍后再来争夺。
在redis中,用setnx命令和del命令,可以简单的实现分布式锁,setnx命令返回1就代表线程抢到锁了,setnx命令返回0代表线程没抢到锁,del命令可以释放锁
package com.yl;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class JedisUtils {
private static JedisPool jedisPool = null;
public static Jedis getJedisObject() {
if (jedisPool == null) {
GenericObjectPoolConfig config = new GenericObjectPoolConfig();
//最大空闲数
config.setMaxIdle(400);
//最大连接数
config.setMaxTotal(2000);
//连接最大等待时间,-1代表没有限制
config.setMaxWaitMillis(300000);
/** * 配置连接池的地址,端口号,超时时间,密码 */
jedisPool = new JedisPool(config,"192.168.244.129",6379,30000,"root123");
}
try {
//通过连接池获取jedis对象
Jedis jedis = jedisPool.getResource();
jedis.auth("root123");
return jedis;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
package com.yl;
import redis.clients.jedis.Jedis;
public class LockTest {
public static void main(String[] args) {
Jedis jedis = JedisUtils.getJedisObject();
Long setnx = jedis.setnx("k1", "v1");
if (setnx == 1) {
//没人占位
//有可能没有执行到del那一步,所以给锁添加过期时间
jedis.expire("k1",5);
jedis.set("name", "yl");
String name = jedis.get("name");
System.out.println(name);
//释放资源
jedis.del("k1");
} else {
//有人占位,停止操作
System.out.println("没有获取到锁!");
}
}
}
从2.0可以看到,获取到锁后,要设置过期时间(设置过期时间是预防业务代码出错,锁一直没释放掉!),那么这里会出现一种情况,假设这个过期时间设得比较小,然后,线程一进来了,执行它自己的业务代码,很复杂,执行得很慢,花费的时间远大于锁的超时时间,这个时候,线程一还在执行自己的业务代码,线程二抢到了锁进来了,然后也执行自己的业务代码,执行到一半,线程一的业务代码执行完了,线程二的还没执行完,线程一按道理还是会去释放锁的,这个时候线程二是锁的拥有者,线程一释放掉线程二的锁,这种情况合理?显然不合理,2.0中锁的value都固定为v1,所以它们公用同一个锁,且它们的value都是一样的!
mkdir lua
cd lua/
vi release.lua
if redis.call("get",KEYS[1])==ARGV[1]then
return redis.call("del",KEYS[1])
else
return 0
end
cat lua/release.lua | redis-cli -a root123 script load --pipe
package com.yl;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;
import java.util.Arrays;
import java.util.UUID;
public class LockTest3 {
public static void main(String[] args) {
Jedis jedis = JedisUtils.getJedisObject();
//1.先随机获取一个字符串
String str= UUID.randomUUID().toString();
//2.获取锁,并且设置过期时间为5秒
String lock = jedis.set("k1", str, new SetParams().nx().ex(5));
//3.是否拿到锁
if (lock != null && "OK".equals(lock)) {
//成功拿到锁
//4.具体业务写这里
jedis.set("name","yl");
jedis.set("age","23");
System.out.println(jedis.get("name"));
System.out.println(jedis.get("age"));
//5.调用lua脚本,释放自己的锁
jedis.evalsha("b8059ba43af6ffe8bed3db65bac35d452f8115d8", Arrays.asList("k1"),Arrays.asList(str));
} else {
//没获取到锁
System.out.println("没有获取到锁!");
}
}
}
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/weixin_41359273/article/details/120661052
内容来源于网络,如有侵权,请联系作者删除!