> 文档中心 > redis分布式锁实现

redis分布式锁实现


单体应用多线程

使用synchronized关键字或ReentrantLock类来保证线程的顺序执行

个别场景使用JDK1.5之后提供的大量原子类AtomicInteger(基于CAS的乐观锁)

测试锁模拟并发可使用闭锁:CountDownLatch类对象模拟

ReentrantLock的公平锁和非公平锁机制

       ReentrantLock有两个构造方法,默认的构造方法中,sync = new NonfairSync();我们可以从字面意思看出它是一个非公平锁。再看看第二个构造方法,它需要传入一个参数,参数是一个布尔型,true是公平锁,false是非公平锁。从上面的源码我们可以看出sync有两个实现类,分别是FairSyncNonfairSync;

  唯一的不同之处在于!hasQueuedPredecessors()这个方法,很明显这个方法是一个队列,由此可以推断,公平锁是将所有的线程放在一个队列中,一个线程执行完成后,从队列中取出下一个线程,而非公平锁则没有这个队列。这些都是公平锁与非公平锁底层的实现原理。

//基于原子类(乐观锁)public class Test {    private AtomicInteger i = new AtomicInteger(0);    public static void main(String[] args) { Test test = new Test(); ExecutorService es = Executors.newFixedThreadPool(50); CountDownLatch cdl = new CountDownLatch(5000); for (int i = 0;i {  test.i.incrementAndGet();  cdl.countDown();     }); } es.shutdown(); try {     cdl.await();     System.out.println("执行完成后,i="+test.i); } catch (InterruptedException e) {     e.printStackTrace(); }    }}//基于synchronized (悲观锁)//使用CountDownLatch模拟并发public class Test {    private int i=0;    public static void main(String[] args) { Test test = new Test(); ExecutorService es = Executors.newFixedThreadPool(50); CountDownLatch cdl = new CountDownLatch(5000); for (int i = 0;i {  //修改部分  开始  synchronized (test){      test.i++;  }  //修改部分  结束  cdl.countDown();     }); } es.shutdown(); try {     cdl.await();     System.out.println("执行完成后,i="+test.i); } catch (InterruptedException e) {     e.printStackTrace(); }    }}//基于ReentrantLock(悲观锁)//使用CountDownLatch模拟并发public class test {    private int i=0;    Lock lock = new ReentrantLock();    public static void main(String[] args) { test test = new test(); //线程池:50个线程 ExecutorService es = Executors.newFixedThreadPool(50); //闭锁 CountDownLatch cdl = new CountDownLatch(5000); for (int i = 0;i {  test.lock.lock();  test.i++;  test.lock.unlock();  cdl.countDown();     }); } es.shutdown(); try {     //等待5000个任务执行完成后,打印出执行结果     cdl.await();     System.out.println("执行完成后,i="+test.i); } catch (InterruptedException e) {     e.printStackTrace(); }    }}

分布式多线程锁

实现方式,一半基于第三方,如:数据库加锁,redis的单线程特性,zk

1.基于mysql数据库sql特性   select.....for update  行锁(排它锁),高并发不适用,数据库压力大

2.redis分布式锁,代码示例如下:

//公共类,锁工具//AutoCloseable 自动关闭资源@Slf4jpublic class RedisLock implements AutoCloseable {    private RedisTemplate redisTemplate;    private String key;    private String value;    //单位:秒    private int expireTime;    public RedisLock(RedisTemplate redisTemplate,String key,int expireTime){ this.redisTemplate = redisTemplate; this.key = key; this.expireTime=expireTime; this.value = UUID.randomUUID().toString();    }    /**     * 获取分布式锁     * @return     */    public boolean getLock(){ RedisCallback redisCallback = connection -> {     //设置NX     RedisStringCommands.SetOption setOption = RedisStringCommands.SetOption.ifAbsent();     //设置过期时间     Expiration expiration = Expiration.seconds(expireTime);     //序列化key     byte[] redisKey = redisTemplate.getKeySerializer().serialize(key);     //序列化value     byte[] redisValue = redisTemplate.getValueSerializer().serialize(value);     //执行setnx操作     Boolean result = connection.set(redisKey, redisValue, expiration, setOption);     return result; }; //获取分布式锁 Boolean lock = (Boolean)redisTemplate.execute(redisCallback); return lock;    }    public boolean unLock() { String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then\n" +  "    return redis.call(\"del\",KEYS[1])\n" +  "else\n" +  "    return 0\n" +  "end"; RedisScript redisScript = RedisScript.of(script,Boolean.class); List keys = Arrays.asList(key); Boolean result = (Boolean)redisTemplate.execute(redisScript, keys, value); log.info("释放锁的结果:"+result); return result;    }    @Override    public void close() throws Exception { unLock();    }}//可起多应用多线程测试//try(){}catch(){}  新特性//AutoCloseable 自动关闭资源@Service@Slf4jpublic class SchedulerService {    @Autowired    private RedisTemplate redisTemplate;    @Scheduled(cron = "0/5 * * * * ?")    public void sendSms(){ try(RedisLock redisLock = new RedisLock(redisTemplate,"autoSms",30)) {     if (redisLock.getLock()){  log.info("向138xxxxxxxx发送短信!");     } } catch (Exception e) {     e.printStackTrace(); }    }}