美文网首页
RedisTemplate实现分布式锁

RedisTemplate实现分布式锁

作者: 山哥Samuel | 来源:发表于2020-02-04 17:52 被阅读0次

    微服务的时代,如果我们有些定时任务要处理,在获取资源的时候,我们要避免重复处理。于是分布式锁在这时候就发挥了重要作用。
    让我们来看看如何用RedisTemplate来实现这个分布式的锁。

    创建Lock帮助类

    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisCallback;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Service;
    
    import java.util.Objects;
    
    /**
     * Description: A Redis Lock Helper
     * User: Samuel Chan
     * Date: 2020-02-04
     * Time: 17:39
     */
    @Service
    public class RedisLockHelper {
    
        public static final String LOCK_PREFIX = "redis_lock";
        public static final int LOCK_EXPIRE = 1000; // ms
    
        @Autowired
        RedisTemplate redisTemplate;
    
    
        /**
         *  Acquire a lock.
         *
         * @param key
         * @return got the lock or not
         */
        public boolean lock(String key){
            String lock = LOCK_PREFIX + key;
    
            return (Boolean) redisTemplate.execute((RedisCallback) connection -> {
    
                long expireAt = System.currentTimeMillis() + LOCK_EXPIRE + 1;
                Boolean acquire = connection.setNX(lock.getBytes(), String.valueOf(expireAt).getBytes());
    
                if (acquire) {
                    return true;
                } else {
    
                    byte[] value = connection.get(lock.getBytes());
    
                    if (Objects.nonNull(value) && value.length > 0) {
    
                        long expireTime = Long.parseLong(new String(value));
    
                        if (expireTime < System.currentTimeMillis()) {
                            // in case the lock is expired
                            byte[] oldValue = connection.getSet(lock.getBytes(), String.valueOf(System.currentTimeMillis() + LOCK_EXPIRE + 1).getBytes());
                            // avoid dead lock
                            return Long.parseLong(new String(oldValue)) < System.currentTimeMillis();
                        }
                    }
                }
                return false;
            });
        }
    
        /**
         * Delete the lock
         *
         * @param key
         */
        public void delete(String key) {
            redisTemplate.delete(key);
        }
    
    
    

    顺便写个单元测试来试试它

    
    import tech.comfortheart.DemoApplication;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.ActiveProfiles;
    import org.springframework.test.context.junit4.SpringRunner;
    
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    @RunWith(SpringRunner.class)
    @SpringBootTest(classes={DemoApplication.class}, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    @ActiveProfiles("test")
    public class RedisLockHelperTests {
        @Autowired
        RedisLockHelper redisHelper;
        @Test
        public void testRedis() throws InterruptedException {
            int totalThreads = 1000;
            ExecutorService executorService = Executors.newFixedThreadPool(totalThreads);
    
            CountDownLatch countDownLatch = new CountDownLatch(totalThreads);
            for(int i=0; i<totalThreads; i++) {
                String threadId = String.valueOf(i);
                executorService.execute( () -> {
                    try {
                        testLock("hey", threadId);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    countDownLatch.countDown();
                });
            }
            countDownLatch.await();
            // After all thread done, acquire again, expect to be successful.
            testLock("hey", "final success");
        }
    
        public void testLock(String key, String threadId) throws InterruptedException {
            boolean lock = redisHelper.lock(key);
            if (lock) {
                System.out.println("Successfully got lock - " + threadId);
                Thread.sleep(2000);
                redisHelper.delete(key);
            } else {
                System.out.println("Failed to obtain lock - " + threadId);
            }
        }
    }
    

    运行了1000个线程,结果还不错:

    2020-02-04 17:30:21.482  INFO 81555 --- [ool-1-thread-11] io.lettuce.core.KqueueProvider           : Starting without optional kqueue library
    Successfully got lock - 95
    Failed to obtain lock - 102
    Failed to obtain lock - 890
    Failed to obtain lock - 83
    Failed to obtain lock - 76
    Failed to obtain lock - 327
    Failed to obtain lock - 893
    Failed to obtain lock - 326
    Failed to obtain lock - 892
    Failed to obtain lock - 78
    Failed to obtain lock - 88
    Failed to obtain lock - 891
    Failed to obtain lock - 882
    Failed to obtain lock - 79
    ...
    Failed to obtain lock - 887
    Failed to obtain lock - 889
    Successfully got lock - final success
    

    相关文章

      网友评论

          本文标题:RedisTemplate实现分布式锁

          本文链接:https://www.haomeiwen.com/subject/hzfdxhtx.html