Springboot集成Redis实现分布式锁

文章目录

  • 集成Redis
    • 引入依赖
    • 配置RedisTemplate
  • 分布式锁
    • 代码实现
    • 测试

集成Redis

引入依赖

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-data-redisartifactId>
dependency>

配置RedisTemplate

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        // 设置Key使用String序列化
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        return redisTemplate;
    }
}

分布式锁

代码实现

public class RedisSimpleLock implements AutoCloseable {
    public static final Logger log = LoggerFactory.getLogger(RedisSimpleLock.class);
    /**
     * 锁key后缀
     */
    public static final String SUFFIX = "~lock";

    private final RedisTemplate<String, Object> redisTemplate;
    /**
     * 锁key
     */
    private final String key;
    /**
     * 超时时间
     */
    private final long timeout;
    /**
     * 时间单位
     */
    private final TimeUnit unit;
    /**
     * 锁状态
     */
    private boolean lock;
    /**
     * 是否自动释放
     */
    private boolean autoUnlock;

    public RedisSimpleLock(RedisTemplate<String, Object> redisTemplate, String key, long timeout, TimeUnit unit) {
        this(redisTemplate, key, timeout, unit, true);
    }

    public RedisSimpleLock(RedisTemplate<String, Object> redisTemplate, String key, long timeout, TimeUnit unit, boolean autoUnlock) {
        this.redisTemplate = redisTemplate;
        this.key = key + SUFFIX;
        this.timeout = timeout;
        this.unit = unit;
        this.autoUnlock = autoUnlock;
    }


    public RedisSimpleLock(RedisTemplate<Object, Object> redisTemplate, String key, long timeout, TimeUnit unit, boolean autoUnlock) {
        this.redisTemplate = redisTemplate;
        this.key = key + SUFFIX;
        this.timeout = timeout;
        this.unit = unit;
        this.autoUnlock = autoUnlock;
    }


    public boolean lock() throws Exception {
        // 利用key过期时间来完成超时自动解锁
        Boolean res = redisTemplate.opsForValue().setIfAbsent(key, "lock", timeout, unit);
        if (res == null) {
            log.info("Redis lock key:{},timeout:{},timeUnit:{} ==> Result:[{}]", key, timeout, unit, "error");
            throw new Exception();
        } else {
            log.info("Redis lock key:{},timeout:{},timeUnit:{} ==> Result:[{}]", key, timeout, unit, res);
            this.lock = res;
            return res;
        }
    }

    private void unlock() {
        if (lock) {
            try {
                Object currentValue = redisTemplate.opsForValue().get(key);
                if (currentValue != null) {
                    redisTemplate.opsForValue().getOperations().delete(key);
                    log.info("Redis unlock key:{} ==> done.", key);
                } else {
                    log.warn("Redis unlock key:{} ==> Key not exists,skip.", key);
                }
            } catch (Exception e) {
                log.error("Redis unlock key:{} ==> An error has occurred.", key, e);
            }
        } else {
            log.info("Redis unlock key:{} ==> skip.", key);
        }
    }

    @Override
    public void close() {
        // 实现AutoCloseable接口close方法,简化try(...)...catch的finally部分,自动调用close方法解锁
        if (autoUnlock) {
            unlock();
        }
    }
}

测试

  1. 测试代码
@RunWith(SpringRunner.class)
@SpringBootTest
public class RedisLockApplicationTests {
    public static final Logger log = LoggerFactory.getLogger(RedisLockApplicationTests.class);

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Test
    public void contextLoads() {
        ExecutorService pool = Executors.newCachedThreadPool(new ThreadFactory() {
            final AtomicInteger threadNumber = new AtomicInteger(1);

            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "RedisLock-" + threadNumber.getAndIncrement());
            }
        });
        // 模拟10个并发
        for (int i = 0; i < 10; i++) {
            pool.submit(() -> {
                try (RedisSimpleLock lock = new RedisSimpleLock(redisTemplate, "TESTLOCK", 30, TimeUnit.SECONDS)) {
                    if (lock.lock()) {
                        log.info("获取锁成功,执行业务逻辑");
                        Thread.sleep(10000);
                        log.info("业务逻辑执行完毕");
                    } else {
                        log.info("未获取到锁");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
        pool.shutdown();
        log.info("等待业务完成");
        while (!pool.isTerminated()) {
        }
        log.info("程序结束");
    }

}
  1. 控制台输出
2019-07-16 23:05:15.826  INFO 3779 --- [    RedisLock-2] io.lettuce.core.EpollProvider            : Starting without optional epoll library
2019-07-16 23:05:15.830  INFO 3779 --- [    RedisLock-2] io.lettuce.core.KqueueProvider           : Starting without optional kqueue library
2019-07-16 23:05:16.136  INFO 3779 --- [    RedisLock-9] c.s.redislock.config.RedisSimpleLock     : Redis lock key:TESTLOCK~lock,timeout:30,timeUnit:SECONDS ==> Result:[true]
2019-07-16 23:05:16.136  INFO 3779 --- [    RedisLock-4] c.s.redislock.config.RedisSimpleLock     : Redis lock key:TESTLOCK~lock,timeout:30,timeUnit:SECONDS ==> Result:[false]
2019-07-16 23:05:16.136  INFO 3779 --- [    RedisLock-7] c.s.redislock.config.RedisSimpleLock     : Redis lock key:TESTLOCK~lock,timeout:30,timeUnit:SECONDS ==> Result:[false]
2019-07-16 23:05:16.136  INFO 3779 --- [    RedisLock-4] c.s.redislock.RedisLockApplicationTests  : 未获取到锁
2019-07-16 23:05:16.136  INFO 3779 --- [    RedisLock-7] c.s.redislock.RedisLockApplicationTests  : 未获取到锁
2019-07-16 23:05:16.136  INFO 3779 --- [    RedisLock-9] c.s.redislock.RedisLockApplicationTests  : 获取锁成功,执行业务逻辑
2019-07-16 23:05:16.137  INFO 3779 --- [    RedisLock-4] c.s.redislock.config.RedisSimpleLock     : Redis unlock key:TESTLOCK~lock ==> skip.
2019-07-16 23:05:16.137  INFO 3779 --- [    RedisLock-7] c.s.redislock.config.RedisSimpleLock     : Redis unlock key:TESTLOCK~lock ==> skip.
2019-07-16 23:05:16.137  INFO 3779 --- [    RedisLock-6] c.s.redislock.config.RedisSimpleLock     : Redis lock key:TESTLOCK~lock,timeout:30,timeUnit:SECONDS ==> Result:[false]
2019-07-16 23:05:16.138  INFO 3779 --- [    RedisLock-6] c.s.redislock.RedisLockApplicationTests  : 未获取到锁
2019-07-16 23:05:16.138  INFO 3779 --- [    RedisLock-3] c.s.redislock.config.RedisSimpleLock     : Redis lock key:TESTLOCK~lock,timeout:30,timeUnit:SECONDS ==> Result:[false]
2019-07-16 23:05:16.138  INFO 3779 --- [    RedisLock-6] c.s.redislock.config.RedisSimpleLock     : Redis unlock key:TESTLOCK~lock ==> skip.
2019-07-16 23:05:16.138  INFO 3779 --- [    RedisLock-3] c.s.redislock.RedisLockApplicationTests  : 未获取到锁
2019-07-16 23:05:16.138  INFO 3779 --- [    RedisLock-3] c.s.redislock.config.RedisSimpleLock     : Redis unlock key:TESTLOCK~lock ==> skip.
2019-07-16 23:05:16.139  INFO 3779 --- [    RedisLock-1] c.s.redislock.config.RedisSimpleLock     : Redis lock key:TESTLOCK~lock,timeout:30,timeUnit:SECONDS ==> Result:[false]
2019-07-16 23:05:16.139  INFO 3779 --- [    RedisLock-1] c.s.redislock.RedisLockApplicationTests  : 未获取到锁
2019-07-16 23:05:16.139  INFO 3779 --- [    RedisLock-1] c.s.redislock.config.RedisSimpleLock     : Redis unlock key:TESTLOCK~lock ==> skip.
2019-07-16 23:05:16.140  INFO 3779 --- [   RedisLock-10] c.s.redislock.config.RedisSimpleLock     : Redis lock key:TESTLOCK~lock,timeout:30,timeUnit:SECONDS ==> Result:[false]
2019-07-16 23:05:16.140  INFO 3779 --- [    RedisLock-5] c.s.redislock.config.RedisSimpleLock     : Redis lock key:TESTLOCK~lock,timeout:30,timeUnit:SECONDS ==> Result:[false]
2019-07-16 23:05:16.140  INFO 3779 --- [   RedisLock-10] c.s.redislock.RedisLockApplicationTests  : 未获取到锁
2019-07-16 23:05:16.140  INFO 3779 --- [    RedisLock-5] c.s.redislock.RedisLockApplicationTests  : 未获取到锁
2019-07-16 23:05:16.140  INFO 3779 --- [    RedisLock-8] c.s.redislock.config.RedisSimpleLock     : Redis lock key:TESTLOCK~lock,timeout:30,timeUnit:SECONDS ==> Result:[false]
2019-07-16 23:05:16.140  INFO 3779 --- [   RedisLock-10] c.s.redislock.config.RedisSimpleLock     : Redis unlock key:TESTLOCK~lock ==> skip.
2019-07-16 23:05:16.140  INFO 3779 --- [    RedisLock-5] c.s.redislock.config.RedisSimpleLock     : Redis unlock key:TESTLOCK~lock ==> skip.
2019-07-16 23:05:16.140  INFO 3779 --- [    RedisLock-8] c.s.redislock.RedisLockApplicationTests  : 未获取到锁
2019-07-16 23:05:16.140  INFO 3779 --- [    RedisLock-8] c.s.redislock.config.RedisSimpleLock     : Redis unlock key:TESTLOCK~lock ==> skip.
2019-07-16 23:05:16.141  INFO 3779 --- [    RedisLock-2] c.s.redislock.config.RedisSimpleLock     : Redis lock key:TESTLOCK~lock,timeout:30,timeUnit:SECONDS ==> Result:[false]
2019-07-16 23:05:16.142  INFO 3779 --- [    RedisLock-2] c.s.redislock.RedisLockApplicationTests  : 未获取到锁
2019-07-16 23:05:16.142  INFO 3779 --- [    RedisLock-2] c.s.redislock.config.RedisSimpleLock     : Redis unlock key:TESTLOCK~lock ==> skip.
2019-07-16 23:05:26.137  INFO 3779 --- [    RedisLock-9] c.s.redislock.RedisLockApplicationTests  : 业务逻辑执行完毕
2019-07-16 23:05:26.145  INFO 3779 --- [    RedisLock-9] c.s.redislock.config.RedisSimpleLock     : Redis unlock key:TESTLOCK~lock ==> done.
2019-07-16 23:05:26.146  INFO 3779 --- [           main] c.s.redislock.RedisLockApplicationTests  : 程序结束

你可能感兴趣的:(springboot,java)