分布式锁一般有三种实现方式:
1. 数据库乐观锁;
2. 基于Redis的分布式锁;
3. 基于ZooKeeper的分布式锁。
分布式锁:当多个进程不在同一个系统中,用分布式锁控制多个进程对资源的访问
分布式锁的使用场景: 线程间并发问题和进程间并发问题都是可以通过分布式锁解决的,但是强烈不建议这样做!
分布式锁的实现(Redis)
几个要用到的redis命令:
setnx(key, value):“set if not exits”,若该key-value不存在,则成功加入缓存并且返回1,否则返回0。
get(key):获得key对应的value值,若不存在则返回nil。
getset(key, value):先获取key对应的value值,若不存在则返回nil,然后将旧的value更新为新的value。
expire(key, seconds):设置key-value的有效期为seconds秒。
代码实现:
依赖引入:
<dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency>
public class RedisPool { private static JedisPool pool;//jedis连接池 private static int maxTotal = 20;//最大连接数 private static int maxIdle = 10;//最大空闲连接数 private static int minIdle = 5;//最小空闲连接数 private static boolean testOnBorrow = true;//在取连接时测试连接的可用性 private static boolean testOnReturn = false;//再还连接时不测试连接的可用性 static { initPool();//初始化连接池 } public static Jedis getJedis(){ return pool.getResource(); } public static void close(Jedis jedis){ jedis.close(); } private static void initPool(){ JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(maxTotal); config.setMaxIdle(maxIdle); config.setMinIdle(minIdle); config.setTestOnBorrow(testOnBorrow); config.setTestOnReturn(testOnReturn); config.setBlockWhenExhausted(true); pool = new JedisPool(config, “127.0.0.1”, 6379, 5000, “liqiyao”); } }
Jedis的api进行封装:
public class RedisPoolUtil { private RedisPoolUtil(){} private static RedisPool redisPool; public static String get(String key){ Jedis jedis = null; String result = null; try { jedis = RedisPool.getJedis(); result = jedis.get(key); } catch (Exception e){ e.printStackTrace(); } finally { if (jedis != null) { jedis.close(); } return result; } } public static Long setnx(String key, String value){ Jedis jedis = null; Long result = null; try { jedis = RedisPool.getJedis(); result = jedis.setnx(key, value); } catch (Exception e){ e.printStackTrace(); } finally { if (jedis != null) { jedis.close(); } return result; } } public static String getSet(String key, String value){ Jedis jedis = null; String result = null; try { jedis = RedisPool.getJedis(); result = jedis.getSet(key, value); } catch (Exception e){ e.printStackTrace(); } finally { if (jedis != null) { jedis.close(); } return result; } } public static Long expire(String key, int seconds){ Jedis jedis = null; Long result = null; try { jedis = RedisPool.getJedis(); result = jedis.expire(key, seconds); } catch (Exception e){ e.printStackTrace(); } finally { if (jedis != null) { jedis.close(); } return result; } } public static Long del(String key){ Jedis jedis = null; Long result = null; try { jedis = RedisPool.getJedis(); result = jedis.del(key); } catch (Exception e){ e.printStackTrace(); } finally { if (jedis != null) { jedis.close(); } return result; } } }
分布式锁工具类:
public class DistributedLockUtil { private DistributedLockUtil(){ } public static boolean lock(String lockName){//lockName可以为共享变量名,也可以为方法名,主要是用于模拟锁信息 System.out.println(Thread.currentThread() + “开始尝试加锁!”); Long result = RedisPoolUtil.setnx(lockName, String.valueOf(System.currentTimeMillis() + 5000)); if (result != null && result.intValue() == 1){ System.out.println(Thread.currentThread() + “加锁成功!”); RedisPoolUtil.expire(lockName, 5); System.out.println(Thread.currentThread() + “执行业务逻辑!”); RedisPoolUtil.del(lockName); return true; } else { String lockValueA = RedisPoolUtil.get(lockName); if (lockValueA != null && Long.parseLong(lockValueA) >= System.currentTimeMillis()){ String lockValueB = RedisPoolUtil.getSet(lockName, String.valueOf(System.currentTimeMillis() + 5000)); if (lockValueB == null || lockValueB.equals(lockValueA)){ System.out.println(Thread.currentThread() + “加锁成功!”); RedisPoolUtil.expire(lockName, 5); System.out.println(Thread.currentThread() + “执行业务逻辑!”); RedisPoolUtil.del(lockName); return true; } else { return false; } } else { return false; } } } }
加锁代码:
public class RedisTool { private static final String LOCK_SUCCESS = "OK"; private static final String SET_IF_NOT_EXIST = "NX"; private static final String SET_WITH_EXPIRE_TIME = "PX"; /** * 尝试获取分布式锁 * @param jedis Redis客户端 * @param lockKey 锁 * @param requestId 请求标识 * @param expireTime 超期时间 * @return 是否获取成功 */ public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) { String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime); if (LOCK_SUCCESS.equals(result)) { return true; } return false; } }
解锁代码:
public class RedisTool { private static final Long RELEASE_SUCCESS = 1L; /** * 释放分布式锁 * @param jedis Redis客户端 * @param lockKey 锁 * @param requestId 请求标识 * @return 是否释放成功 */ public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) { 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(lockKey), Collections.singletonList(requestId)); if (RELEASE_SUCCESS.equals(result)) { return true; } return false; } }
基于redisson分布式锁
配置文件:
<dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.11.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
@Autowired private RedissonClient redissonClient; @RequestMapping("/redissonLock") public String redissonLock() throws InterruptedException { RLock rLock = redissonClient.getLock("order"); log.info("我进入了方法"); rLock.lock(30, TimeUnit.SECONDS); log.info("获得了锁"); Thread.sleep(15000); log.info("释放了锁"); rLock.unlock(); log.info("方法执行完成"); return "方法执行完成"; }
文章下方有交流学习区!一起学习进步!也可以前往官网,加入官方微信交流群
创作不易,如果觉得文章不错,可以点赞收藏评论
你的支持和鼓励是我创作的动力❗❗❗
官网:Doker 多克;官方旗舰店:首页-Doker 多克-淘宝网 全品优惠