自己写了个简单的redis分布式锁
【注意:此锁需要在每次使用前都创建对象,也就是要在线程内每次都创建对象后使用】
package redis; import java.util.Collections; import java.util.Random; import java.util.UUID; import org.redisson.Redisson; import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.redisson.config.Config; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; public class RedisLock2 { JedisPool jedisPool; private String key; private String value; //默认锁超时时间5秒 private Long timeout = 5L; //加锁成功的时间起点 private Long startTime; /** * 使用默认超时时间 * @param JedisPool * @param key */ public RedisLock2(JedisPool JedisPool,String key) { super(); this.jedisPool = JedisPool; this.key = key; value = System.currentTimeMillis()+""; } /** * 单独设置超时时间 * @param JedisPool * @param key 锁的名称 * @param timeout 锁超时时间 */ public RedisLock2(JedisPool JedisPool,String key,Long timeout) { super(); this.jedisPool = JedisPool; this.key = key; this.timeout = timeout; value = UUID.randomUUID().toString(); // System.out.println("创建锁时的value:"+value); } /** * 单次加锁,需要判断返回值【适用于获取不到锁就返回的业务场景】 * @return true:加锁成功; false:加锁失败 */ public boolean lock() { Jedis jedis = jedisPool.getResource(); boolean ok = jedis.set(key,value, "nx", "ex", timeout)!=null; jedis.close(); if (!ok) { //加锁失败 return false; } //加锁成功 startTime = System.currentTimeMillis(); return true; } /** * 阻塞重试加锁,默认时间间隔10毫秒 */ public void lockRetry() { lockRetry(null); } /** * 阻塞重试加锁,直到加锁成功【适用于一定要执行的业务】 * @param interval 重试时间间隔毫秒值 */ public void lockRetry(Long interval) { if (interval==null) { //默认10毫秒重试一次 interval = 10L; }else if(interval==0) { //如果传值为0,则取10以内随机值 interval = (long) (new Random().nextInt(10) + 1); } int num = 0; while (true) { boolean b = lock(); num++; if (b) { System.out.println("加锁 "+num+" 次后成功"); break; } try { //休息10毫秒后重试 Thread.sleep(interval); System.out.println("有并发,休息:"+interval+"毫秒"); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * 解锁【首先尝试用lua方式,如果redis的版本不支持lua,用普通方式】 * @return */ public String unlock() { //解锁返回消息(非正常解锁时会打印,还可以在业务中解锁时判断此返回值) String msg = ""; //加锁到解锁消耗时间 long timeConsume = System.currentTimeMillis() - startTime; //超时直接返回 if (timeConsume > timeout * 1000) { System.out.println("出现超时解锁-----------key:" + key + ",耗时毫秒:" + timeConsume); // return false; msg = "出现超时解锁--key:" + key + ",耗时毫秒:" + timeConsume; return msg; } //这里是为了避免超时后,释放掉其他线程的同名锁 String luaScript = "if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end"; Jedis jedis = jedisPool.getResource(); //执行lua脚本返回值 Object evalRtn = 0; try { evalRtn = jedis.eval(luaScript, Collections.singletonList(key), Collections.singletonList(value)); // System.out.println("lua解锁返回值:"+evalRtn); } catch (Exception e) { e.printStackTrace(); //如果当前redis不支持lua脚本,用下面方法,但是下面的代码不是原子操作,可能会有并发问题,这里忽略 String s = jedis.get(key); if (value.equals(s)) { //释放锁 jedis.del(key); // return true; msg = "解锁成功"; return msg; } }finally { jedis.close(); } if ((Long) evalRtn == 1) { // return true; msg = "解锁成功"; return msg; } System.out.println("出现其他异常解锁失败---------key:" + key); // return false; msg = "出现其他异常解锁失败--key:" + key; return msg; } //简单加锁测试 public static void main1(String[] args) throws InterruptedException { JedisPool jedisPool = new JedisPool("127.0.0.1", 6379); //设置超时时间100秒 RedisLock2 redisLock = new RedisLock2(jedisPool,"lock1",100L); //加锁 boolean lock = redisLock.lock(); //获取到锁才执行,获取不到不执行 if(lock) { //执行业务逻辑(要保证业务逻辑执行时间小于锁超时时间) System.out.println("我获取到锁了"); // Thread.sleep(2000); } System.out.println(redisLock.unlock()); } //开启多线程往redis中设置值,保证不覆盖(用单次锁在业务逻辑中循环阻塞) public static void main2(String[] args) { JedisPool jedisPool = new JedisPool("127.0.0.1", 6379); //原来是使用网上的一种锁 // DistributedLock lock = new DistributedLock(jedisPool); for (int i = 0; i < 3; i++) { // final int k = i; new Thread(new Runnable() { @Override public void run() { for (int j = 0; j < 10; j++) { int k = j; // 连接本地的 Redis 服务 // Jedis jedis = new Jedis("localhost"); // String code = null; RedisLock2 myLock = null; try { //加分布式锁 // code = lock.lock("mylock"); myLock = new RedisLock2(jedisPool,"lock1",10L); while (true) { //不断获取锁 boolean lock = myLock.lock(); if (lock) { //如果获取到则执行 // 从连接池中获取一个jedis对象 Jedis jedis = jedisPool.getResource(); if (!jedis.exists("a" + k)) { jedis.set("a" + k, Thread.currentThread().getName()); jedis.expire("a" + k, 60); System.out.println(System.currentTimeMillis() + "--" + Thread.currentThread().getName() + "--key:" + ("a" + k) + "不存在,设置值为: " + Thread.currentThread().getName()); try { // Thread.sleep((long) (Math.random()*1000)); } catch (Exception e) { e.printStackTrace(); } } else { System.out.println(Thread.currentThread().getName() + "--key:" + ("a" + k) + "存在,值为: " + jedis.get("a" + k)); } jedis.close(); break;//跳出循环 } } } finally { //释放分布式锁 // lock.unLock("mylock",code); //执行完解锁 myLock.unlock(); } } } }).start(); } } //开启多线程往redis中设置值,保证不覆盖(用阻塞锁) public static void main(String[] args) { //原来是使用rdissen锁 //获取redisson // Config config = new Config(); // config.useSingleServer().setAddress("redis://localhost:6379"); // RedissonClient redisson = Redisson.create(config); // //获取锁 // RLock lock = redisson.getLock("mylock"); // 创建连接池对象 JedisPool jedisPool = new JedisPool("127.0.0.1", 6379); for (int i = 0; i < 3; i++) { // final int k = i; new Thread(new Runnable() { @Override public void run() { for (int j = 0; j < 10; j++) { int k = j; // 连接本地的 Redis 服务 // Jedis jedis = new Jedis("localhost"); RedisLock2 myLock = null; try { //加redisson分布式锁 // lock.lock(); //用我们自己写的重试锁【自定义锁非可重入锁,需要在线程中每次使用时都创建一个锁对象,多线程中只要名称相同就认为是同一个锁】 myLock = new RedisLock2(jedisPool,"lock1",10L); //开启阻塞锁 myLock.lockRetry(3L); //执行业务逻辑【因为用阻塞锁,无需判断返回值】 // 从连接池中获取一个jedis对象 Jedis jedis = jedisPool.getResource(); if (!jedis.exists("a" + k)) { jedis.set("a" + k, Thread.currentThread().getName()); jedis.expire("a" + k, 60); // System.out.println(System.currentTimeMillis() + "--" + Thread.currentThread().getName() // + "--key:" + ("a" + k) + "不存在,设置值为: " + Thread.currentThread().getName()); try { // Thread.sleep((long) (Math.random()*1000)); } catch (Exception e) { e.printStackTrace(); } } else { // System.out.println(Thread.currentThread().getName() + "--key:" + ("a" + k) + "存在,值为: " // + jedis.get("a" + k)); } jedis.close(); } finally { //释放redisson分布式锁 // lock.unlock(); //用我自己定义的锁 myLock.unlock(); } } } }).start(); } } }