一、Jedis的简单创建
package com.app.redis; import com.app.redis.lock.RedisWithLock; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; import redis.clients.jedis.params.SetParams; import java.util.Arrays; import java.util.List; /** * @author:wuqi * @date:2020/2/8 * @description:com.app.redis * @version:1.0 */ public class RedisUtils { /** * 创建单例 */ private RedisUtils() throws IllegalAccessException { throw new IllegalAccessException(); } // private static Jedis JEDIS = null; private static JedisPool jedisPool = null; private static final String HOST = "192.168.0.114"; static{ // JEDIS = new Jedis(HOST,6379,1000); JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(100); config.setMaxIdle(100); config.setMaxWaitMillis(10000); config.setTestOnBorrow(true); jedisPool = new JedisPool(config,HOST,6379); } private static Jedis getJedis(){ // return jedis; Jedis jedis = jedisPool.getResource(); if (jedis != null){ return jedis; }else { jedis = jedisPool.getResource(); if(jedis != null){ return jedis; } return new Jedis(HOST,6379,1000); } } /** * 测试连接 */ public static void main(String[] args){ Jedis jedis = null; try { jedis = getJedis(); jedis.set("linkTest2","hello World2"); String back = jedis.set("linkTest","hello World"); System.out.println(("OK").equals(back)); Object response = RedisUtils.eval(RedisWithLock.UNLOCK_EVAL, Arrays.asList("linkTest","linkTest2"), Arrays.asList("hello World","hello World2")); System.out.println(response); }finally { if(jedis != null){ //释放jedispool的一个连接 jedis.close(); } //关闭jedispool close(); } } /** * 封装方法 */ interface CallWithRedis<T>{ public T call(Jedis jedis); } private static <T> T execute(CallWithRedis<T> caller){ try (Jedis jedis = getJedis()){ return caller.call(jedis); } } public static String set(String key, String value, SetParams params){ return RedisUtils.execute(new CallWithRedis<String>() { @Override public String call(Jedis jedis) { return jedis.set(key,value,params); } }); } public static String get(String key){ return RedisUtils.execute(new CallWithRedis<String>() { @Override public String call(Jedis jedis) { return jedis.get(key); } }); } public static Long del(String key){ return RedisUtils.execute(new CallWithRedis<Long>() { @Override public Long call(Jedis jedis) { return jedis.del(key); } }); } public static Long expire(String key, int seconds){ return RedisUtils.execute(new CallWithRedis<Long>() { @Override public Long call(Jedis jedis) { return jedis.expire(key,seconds); } }); } public static Object eval(String script, List<String> keys, List<String> value){ return RedisUtils.execute(new CallWithRedis<Object>() { @Override public Object call(Jedis jedis){ return jedis.eval(script,keys,value); } }); } public static void close(){ if(jedisPool != null){ jedisPool.close(); } } }
二、单机下分布式锁
redis是单线程的,所以指令都是原子操作,可实现分布式锁(乐观锁,类似于CAS自旋锁)。
JVM锁synchronize和Lock是计数器实现,其中Lock用CAS自旋锁实现代码块的原子性来保证线程安全,
redis实现分布式锁类似于CAS自旋锁实现,用setnx原子操作自旋实现代码块原子性来保证线程安全,但有几点需要注意。这篇博客描述的很详细
自己实现的一个单机redis下分布式锁
public class RedisWithLock implements Lock { private String key; private ThreadLocal<String> valueLocal = new ThreadLocal(); /** * KEYS[1] == Arrays.asList(key).get(0) * ARGV[1] == Arrays.asList(value).get(0) */ public static final String UNLOCK_EVAL = "if redis.call('get',KEYS[1]) == ARGV[1] then " + " return redis.call('del',KEYS[1]) " + "else " + " return 0 " + " end"; public RedisWithLock(String key){ this.key = key; } @Override public void lock() { String value = new Random().nextLong() + ""; valueLocal.set(value); for (;;){ if ("OK".equals(RedisUtils.set(key,valueLocal.get(),SetParams.setParams().nx().ex(5)))){ String val = valueLocal.get(); //创建守护线程 unlock前刷新expire时间 Thread thread = new Thread(new Runnable() { @Override public void run() { try { String lock = null; for(;;){ lock = RedisUtils.get(key); if(lock != null && lock.equals(val)){ RedisUtils.expire(key,5); Thread.sleep(4000); }else{ break; } } } catch (InterruptedException e) { e.printStackTrace(); } } }); thread.setDaemon(true); thread.start(); break; } } } @Override public void unlock() { Object response = RedisUtils.eval(UNLOCK_EVAL, Arrays.asList(key), Arrays.asList(valueLocal.get())); if(Integer.valueOf(response.toString()) == 0){ System.out.println("解锁失败"); } valueLocal.remove(); } @Override public void lockInterruptibly() throws InterruptedException { throw new UnsupportedOperationException(); } @Override public boolean tryLock() { throw new UnsupportedOperationException(); } @Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { throw new UnsupportedOperationException(); } @Override public Condition newCondition() { throw new UnsupportedOperationException(); } } //模拟服务A public class AppA { public static void main(String[] args){ RedisWithLock lock = new RedisWithLock("lock"); ExecutorService pool = Executors.newFixedThreadPool(2); pool.submit(new Runnable() { @Override public void run() { lock.lock(); try { System.out.println("appA thread1 lock :"+DateUtils.format(System.currentTimeMillis())); Thread.sleep(10000); }catch (InterruptedException e){ e.printStackTrace(); }finally { lock.unlock(); System.out.println("appA thread1 unlock :"+DateUtils.format(System.currentTimeMillis())); } } }); pool.submit(new Runnable() { @Override public void run() { lock.lock(); try { System.out.println("appA thread2 lock :"+DateUtils.format(System.currentTimeMillis())); Thread.sleep(3000); }catch (InterruptedException e){ e.printStackTrace(); }finally { lock.unlock(); System.out.println("appA thread2 unlock :"+DateUtils.format(System.currentTimeMillis())); } } }); pool.shutdown(); while (!pool.isTerminated()){ } RedisUtils.close(); } } //模拟服务B public class AppB { public static void main(String[] args){ RedisWithLock lock = new RedisWithLock("lock"); ExecutorService pool = Executors.newFixedThreadPool(2); pool.submit(new Runnable() { @Override public void run() { lock.lock(); try { System.out.println("appB thread1 lock :"+DateUtils.format(System.currentTimeMillis())); Thread.sleep(3000); }catch (InterruptedException e){ e.printStackTrace(); }finally { lock.unlock(); System.out.println("appB thread1 unlock :"+DateUtils.format(System.currentTimeMillis())); } } }); pool.submit(new Runnable() { @Override public void run() { lock.lock(); try { System.out.println("appB thread2 lock :"+DateUtils.format(System.currentTimeMillis())); Thread.sleep(3000); }catch (InterruptedException e){ e.printStackTrace(); }finally { lock.unlock(); System.out.println("appB thread2 unlock :"+DateUtils.format(System.currentTimeMillis())); } } }); pool.shutdown(); while (!pool.isTerminated()){ } RedisUtils.close(); } }
三、集群下分布式锁——redlock(红锁)
单机下实现的分布式锁,在集群下不是绝对线程安全的,例如一个客户端在主节点中申请成功一把锁,主节点还未来得及同步到从节点,主节点突然挂掉了(基本不会发生),从节点变成主节点,此时新主节点是没有锁的,当另一个客户端申请锁会成功。未解决这个问题Antirez发明了redlock算法:
redlock算法:提供多个redis实例,实例之间相互独立,没有主从关系,加锁时过半redis实例set成功就认为加锁成功,释放锁时一样。
redlock相比于单机的分布式锁,由于需要向多个节点进行读写,所以性能会低一些,使用哪种锁需要慎重考虑,redlock的不安全性 https://www.cnblogs.com/baichunyu/p/11631777.html
参考《Redis深度历险》