首页 > Redis > Redis实现分布式锁

Redis实现分布式锁

大家都知道Redis是NoSQL的一种,目前在互联网公司中在作为缓存广泛的使用者,其实利用Redis的setnx还可以快速实现一个分布式锁,公司的业务就需要使用分布式锁保证数据的唯一性,经检索在网上发现已经有活雷锋分享了一套,本着不在重新发明轮子的想法,测试了一下好像没有问题,几乎不用对原代码进行修改,就能使用,下面就分享在这里,供需要的朋友参考。原文里面还有对实现的原理进行解释,所以本文就不再赘述,详情请通过参考资料进行访问。


package cn.bridgeli.distributedlock;

import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Transaction;

public class RedisDistributedLock {

    private static final Logger LOG = LoggerFactory.getLogger(RedisDistributedLock.class);

    private static final String redisHost = "127.0.0.1";

    private static final int port = 6381;

    private static JedisPoolConfig config;

    private static JedisPool pool;

    private static ExecutorService service;

    private static int ThLeng = 10;

    private static CountDownLatch latch;

    private static AtomicInteger Countor = new AtomicInteger(0);

    private static int count = 0;

    private static String LockName = "mylock_test10";

    static {
        // 利用Redis连接池,保证多个线程利用多个连接,充分模拟并发性
        config = new JedisPoolConfig();
        config.setMaxIdle(10);
        config.setMaxWaitMillis(1000);
        config.setMaxTotal(30);
        pool = new JedisPool(config, redisHost, port);
        // 利用ExecutorService 管理线程
        service = Executors.newFixedThreadPool(ThLeng);
        // CountDownLatch保证主线程在全部线程结束之后退出
        latch = new CountDownLatch(ThLeng);
    }

    /**
     * 获取锁 tips:生成一个UUID,作为Key的标识,不断轮询lockName,直到set成功,表示成功获取锁。
     * 其他的线程在set此lockName时被阻塞直到超时。
     * 
     * @param pool
     * @param lockName
     * @param timeouts
     * @return 锁标志
     */
    public static String getLock(JedisPool pool, String lockName, long timeouts) {
        Jedis client = pool.getResource();
        try {
            String value = UUID.randomUUID().toString();
            long timeWait = System.currentTimeMillis() + timeouts * 1000;
            while (System.currentTimeMillis() < timeWait) {
                if (client.setnx(lockName, value) == 1) {
                    // 防止key卡死,一直不释放锁,所以1800s过期
                    client.expire(lockName, 1800);
                    LOG.info("lock geted");
                    return value;
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            LOG.info("get lock timeouts");
        } finally {
            // pool.returnBrokenResource(client);
            pool.returnResource(client);
        }
        return null;
    }

    /**
     * 释放锁 tips:对lockName做watch,开启一个事务,删除以LockName为key的锁,删除后,此锁对于其他线程为可争抢的。
     * 
     * @param pool
     * @param lockName
     * @param value
     */
    public static void relaseLock(JedisPool pool, String lockName, String value) {
        Jedis client = pool.getResource();
        try {
            while (true) {
                client.watch(lockName);
                if (client.get(lockName).equals(value)) {
                    Transaction tx = client.multi();
                    tx.del(lockName);
                    tx.exec();
                    return;
                }
                client.unwatch();
            }
        } finally {
            // pool.returnBrokenResource(client);
            pool.returnResource(client);
        }
    }

    public static void main(String args[]) {
        for (int i = 0; i < ThLeng; i++) {
            String tName = "thread-" + i;
            Thread t = new Thread(new SubAddThread(pool, tName));
            LOG.info(tName + "inited...");
            service.submit(t);
        }
        service.shutdown();
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        LOG.info(Countor.get() + "");
        LOG.info(count + "");
    }

    public static class SubAddThread implements Runnable {

        private String name;

        private JedisPool pool;

        public SubAddThread(JedisPool pool, String uname) {
            this.pool = pool;
            this.name = uname;
        }

        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                LOG.info(name + " starting...");
                String valuse = getLock(pool, LockName, 50);
                LOG.info(name + " get Lock " + valuse);
                count++;
                relaseLock(pool, LockName, valuse);
                Countor.incrementAndGet();
                LOG.info(name + " " + count);
            }
            latch.countDown();
            LOG.info(name + " complated");
        }

    }
}

参考资料:http://www.jianshu.com/p/c1f5d26cb1c9

分享到:
作 者: BridgeLi,http://www.bridgeli.cn/
原文链接:https://www.bridgeli.cn/archives/326
版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。
分类: Redis 标签: ,
  1. 本文目前尚无任何评论.