Redis分布式锁Java实现

Redis分布式锁Java实现

redis分布式锁可以解决多个应用进程间同步操作的一致性。网上有很多资料并不能完全解决。

  • 1.时间同步问题
  • 2.在一个进程crash后失效时间后自动释放锁
  • 3.有些多线程race condition没有考虑到

Java版本的代码参考如下

package com.wdzj.jedis.distribution.test;

import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.exceptions.JedisException;

/**
 * Jedis实现分布式锁
 * 
 * @author hello
 *
 */
public class JedisDistributionLock {

    private final static long ACCQUIRE_LOCK_TIMEOUT_IN_MS = 10 * 1000;

    private final static int EXPIRE_IN_SECOND = 5;//锁失效时间

    private final static long WAIT_INTERVAL_IN_MS = 100;

    private final JedisPool jedisPool;
    private final long acquireLocktimeoutInMS;
    private final int expireInSecond;
    private final long waitIntervalInMS;
    private final ConcurrentMap<String, String> settedKeys;

    public JedisDistributionLock(final JedisPool jedisPool,
            final long acquireLocktimeout, final int expireInSecond,
            final long waitIntervalInMS) {
        this.jedisPool = jedisPool;
        this.acquireLocktimeoutInMS = acquireLocktimeout;
        this.expireInSecond = expireInSecond;
        this.waitIntervalInMS = waitIntervalInMS;
        this.settedKeys = new ConcurrentHashMap<String, String>();
    }

    public JedisDistributionLock(final JedisPool jedisPool) {
        this(jedisPool, ACCQUIRE_LOCK_TIMEOUT_IN_MS, EXPIRE_IN_SECOND,
                WAIT_INTERVAL_IN_MS);
    }

    public void lock(final String redisKey) throws Exception {
        validateRedisKeyName(redisKey);
        Jedis resource = null;
        try {
            resource = jedisPool.getResource();
            long timeoutAt = currentTimeMillisFromRedis()
                    + acquireLocktimeoutInMS;
            boolean flag = false;
            while (true) {
                String expireAt = String.valueOf(currentTimeMillisFromRedis()
                        + expireInSecond * 1000);
                long ret = resource.setnx(redisKey, expireAt);
                if (ret == 1) {
                    settedKeys.put(redisKey, expireAt);
                    flag = true;
                    break;
                } else {
                    String oldExpireAt = resource.get(redisKey);
                    if (oldExpireAt != null
                            && Long.parseLong(oldExpireAt) < currentTimeMillisFromRedis()) {
                        oldExpireAt = resource.getSet(redisKey, expireAt);

                        if (Long.parseLong(oldExpireAt) < currentTimeMillisFromRedis()) {
                            settedKeys.put(redisKey, expireAt);

                            flag = true;
                            break;
                        } else {
                            // loop ...
                        }
                    } else {
                        // loop ...
                    }
                }

                if (acquireLocktimeoutInMS <= 0
                        || timeoutAt < currentTimeMillisFromRedis()) {
                    break;
                }

                try {
                    TimeUnit.MILLISECONDS.sleep(waitIntervalInMS);
                } catch (Exception ignore) {
                }
            }
            if (!flag) {
                throw new RuntimeException("canot acquire lock now ...");
            }
        } catch (JedisException je) {
            je.printStackTrace();
            if (resource != null) {
                jedisPool.returnBrokenResource(resource);
            }
            throw je;
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (resource != null) {
                jedisPool.returnResource(resource);
            }
        }
    }

    public boolean unlock(final String name) throws Exception {
        validateRedisKeyName(name);
        Jedis resource = null;
        try {
            resource = jedisPool.getResource();
            resource.del(name);
            settedKeys.remove(name);
            return true;
        } catch (JedisException je) {
            je.printStackTrace();
            if (resource != null) {
                jedisPool.returnBrokenResource(resource);
            }
            return false;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            if (resource != null) {
                jedisPool.returnResource(resource);
            }
        }
    }

    public boolean unlockAll() throws Exception {
        Jedis resource = null;
        try {
            resource = jedisPool.getResource();
            Iterator<String> iter = settedKeys.keySet().iterator();
            while (iter.hasNext()) {
                String key = iter.next();
                resource.del(key);
                settedKeys.remove(key);
            }
            return true;
        } catch (JedisException je) {
            je.printStackTrace();
            if (resource != null) {
                jedisPool.returnBrokenResource(resource);
            }
            return false;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            if (resource != null) {
                jedisPool.returnResource(resource);
            }
        }
    }

    private void validateRedisKeyName(String name) {
        if (name == null || "".equals(name.trim())) {
            throw new IllegalArgumentException("validateKey fail.");
        }
    }

    private Long currentTimeMillisFromRedis() throws Exception {
        Jedis resource = null;
        try {
            resource = jedisPool.getResource();
            return Long.parseLong(resource.time().get(0)) * 1000;
        } catch (JedisException je) {
            je.printStackTrace();
            if (resource != null) {
                jedisPool.returnBrokenResource(resource);
            }
            throw je;
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            if (resource != null) {
                jedisPool.returnResource(resource);
            }
        }
    }

    public static void main(String[] args) throws Exception {
        ExecutorService executorService = Executors.newCachedThreadPool();
        GenericObjectPoolConfig config = new GenericObjectPoolConfig();
        config.setMaxIdle(200);
        config.setMaxTotal(200);
        JedisPool pool = new JedisPool(config, "192.168.8.21", 6379, 3000,
                "########");
        final JedisDistributionLock jedisDistributionLock = new JedisDistributionLock(
                pool);

        final int threadNum = 8;
        final CountDownLatch countDownLatch = new CountDownLatch(threadNum);
        final int reqsPerThread = 10;
        final AtomicLong seq = new AtomicLong(0);
        final String key = "wdzj";

        for (int i = 0; i < threadNum; i++) {
            executorService.submit(new Runnable() {
                public void run() {
                    System.out.println(Thread.currentThread().getId()
                            + " start...");
                    try {
                        for (int j = 0; j < reqsPerThread; j++) {
                            jedisDistributionLock.lock(key);
                            System.out.println(seq.incrementAndGet());
                            jedisDistributionLock.unlock(key);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    } finally {
                        countDownLatch.countDown();
                    }
                }

            });
        }

        countDownLatch.await();

        System.out.println("---------------------------->" + seq.longValue());
        if (threadNum * reqsPerThread == seq.longValue()) {
            System.out.println("-------------ok-----------");
        } else {
            System.err.println("-------------err-----------");
        }
    }

}

参考资料
Redis分布式锁Java实现
http://www.cnblogs.com/wuhuajun/p/5242644.html
http://www.cnblogs.com/it-cen/p/4984272.html

### Java 实现 Redis 分布式锁 #### 使用 Jedis 客户端实现分布式锁 下面展示了一个利用 `Jedis` 库操作 Redis 来创建分布式锁的方法: ```java import redis.clients.jedis.Jedis; public class DistributedLock { /** * 尝试获取分布式锁. * * @param jedis 连接实例 * @param lockKey 锁名称对应的key * @param requestId 请求唯一标识符 * @param expireTime 锁的有效期(毫秒) * @return 是否成功获得锁 */ public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) { String result = jedis.set(lockKey, requestId, "NX", "PX", expireTime); return "OK".equals(result); // 如果返回值为 OK 表明加锁成功[^3] } /** * 释放分布式锁. * * @param jedis 连接实例 * @param lockKey 锁名称对应的key * @param requestId 请求唯一标识符 */ public static void releaseDistributedLock(Jedis jedis, String lockKey, String requestId) { String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then " + "return redis.call('del', KEYS[1]) " + "else " + "return 0 end"; jedis.eval(luaScript, java.util.Collections.singletonList(lockKey), java.util.Collections.singletonList(requestId)); } } ``` 这段代码定义了两个主要函数:一个是用来尝试获取锁的 `tryGetDistributedLock()` 函数,另一个是用来安全地释放锁的 `releaseDistributedLock()` 函数。为了确保只有一个客户端能够删除自己拥有的锁而不会影响到其他的锁,在解锁的时候采用了 Lua 脚本来原子化地完成这个过程。 当调用者想要获取某个资源上的独占访问权时,应该先调用 `tryGetDistributedLock()` 方法去请求锁;一旦获得了锁就可以放心大胆地对该共享资源进行读取或修改操作;处理完毕之后记得要尽快调用 `releaseDistributedLock()` 去主动释放掉所持之锁以便让其他等待中的进程有机会继续运行下去[^4]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值