基于Redis实现分布式锁(下)

来源:互联网 发布:全轮转可变数据印刷机 编辑:程序博客网 时间:2024/06/05 20:00

基于Redis实现分布式锁,上一篇已经介绍了原理和流程,下面是实现的代码。

Redis分布式锁

package com.github.jedis.lock;import redis.clients.jedis.Jedis;/** * Redis distributed lock implementation. */public class JedisLock {    Jedis jedis;    /**     * Lock key path.     */    String lockKey;    /**     * Lock expiration in miliseconds.     * 锁超时,防止线程在入锁以后,无限的执行等待     */    int expireMsecs = 60 * 1000;    /**     * Acquire timeout in miliseconds.     * 锁等待,防止线程饥饿     */    int timeoutMsecs = 10 * 1000;    boolean locked = false;    /**     * Detailed constructor with default acquire timeout 10000 msecs and lock expiration of 60000 msecs.     *     * @param jedis     * @param lockKey lock key (ex. account:1, ...)     */    public JedisLock(Jedis jedis, String lockKey) {        this.jedis = jedis;        this.lockKey = lockKey;    }    /**     * Detailed constructor with default lock expiration of 60000 msecs.     *     * @param jedis     * @param lockKey      lock key (ex. account:1, ...)     * @param timeoutMsecs acquire timeout in miliseconds (default: 10000 msecs)     */    public JedisLock(Jedis jedis, String lockKey, int timeoutMsecs) {        this(jedis, lockKey);        this.timeoutMsecs = timeoutMsecs;    }    /**     * Detailed constructor.     *     * @param jedis     * @param lockKey      lock key (ex. account:1, ...)     * @param timeoutMsecs acquire timeout in miliseconds (default: 10000 msecs)     * @param expireMsecs  lock expiration in miliseconds (default: 60000 msecs)     */    public JedisLock(Jedis jedis, String lockKey, int timeoutMsecs, int expireMsecs) {        this(jedis, lockKey, timeoutMsecs);        this.expireMsecs = expireMsecs;    }    /**     * Detailed constructor with default acquire timeout 10000 msecs and lock expiration of 60000 msecs.     *     * @param lockKey lock key (ex. account:1, ...)     */    public JedisLock(String lockKey) {        this(null, lockKey);    }    /**     * Detailed constructor with default lock expiration of 60000 msecs.     *     * @param lockKey      lock key (ex. account:1, ...)     * @param timeoutMsecs acquire timeout in miliseconds (default: 10000 msecs)     */    public JedisLock(String lockKey, int timeoutMsecs) {        this(null, lockKey, timeoutMsecs);    }    /**     * Detailed constructor.     *     * @param lockKey      lock key (ex. account:1, ...)     * @param timeoutMsecs acquire timeout in miliseconds (default: 10000 msecs)     * @param expireMsecs  lock expiration in miliseconds (default: 60000 msecs)     */    public JedisLock(String lockKey, int timeoutMsecs, int expireMsecs) {        this(null, lockKey, timeoutMsecs, expireMsecs);    }    /**     * @return lock key     */    public String getLockKey() {        return lockKey;    }    /**     * Acquire lock.     *     * @return true if lock is acquired, false acquire timeouted     * @throws InterruptedException in case of thread interruption     */    public synchronized boolean acquire() throws InterruptedException {        return acquire(jedis);    }    /**     * Acquire lock.     *     * @param jedis     * @return true if lock is acquired, false acquire timeouted     * @throws InterruptedException in case of thread interruption     */    public synchronized boolean acquire(Jedis jedis) throws InterruptedException {        //锁等待,防止线程饥饿        int timeout = timeoutMsecs;        while (timeout >= 0) {            //锁超时,防止线程在入锁以后,无限的执行等待(为什么加1:因为可能有相同时间的操作,这样做不完美,但是实用)            long expires = System.currentTimeMillis() + expireMsecs + 1;            String expiresStr = String.valueOf(expires); //锁到期时间            if (jedis.setnx(lockKey, expiresStr) == 1) {                // lock acquired                locked = true;                return true;            }            String currentValueStr = jedis.get(lockKey); //redis里的时间            if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {                //判断是否为空,不为空的情况下,如果被其他线程设置了值,则第二个条件判断是过不去的                // lock is expired                String oldValueStr = jedis.getSet(lockKey, expiresStr);                //获取上一个锁到期时间,并设置现在的锁到期时间,                //只有一个线程才能获取上一个线上的设置时间,因为jedis.getSet是同步的                if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {                    //如过这个时候,多个线程恰好都到了这里,但是只有一个线程的设置值和当前值相同,他才有权利获取锁                    // lock acquired                    locked = true;                    return true;                }            }            timeout -= 100;            Thread.sleep(100);        }        return false;    }    /**     * Acqurired lock release.     */    public synchronized void release() {        release(jedis);    }    /**     * Acqurired lock release.     */    public synchronized void release(Jedis jedis) {        if (locked) {            jedis.del(lockKey);            locked = false;        }    }}

分布式锁封装

<pre name="code" class="java">/** *  Copyright (c)  2011-2020 Panguso, Inc. *  All rights reserved. * *  This software is the confidential and proprietary information of Panguso,  *  Inc. ("Confidential Information"). You shall not *  disclose such Confidential Information and shall use it only in *  accordance with the terms of the license agreement you entered into with Panguso. */package com.chinaso.phl.concurrent;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisPool;import com.github.jedis.lock.JedisLock;/** * 分布式锁的简单用法 * @author  come from network * @date 2014-3-7 */public class SimpleLock {    private static Logger    logger = LoggerFactory.getLogger(SimpleLock.class);    private static JedisPool pool;    private JedisLock        jedisLock;    private String           lockKey;    private Jedis            jedis;    private int              timeoutMsecs;    private int              expireMsecs;    public SimpleLock(String lockKey) {        this(lockKey, 3000, 300000);    }    public SimpleLock(String lockKey, int timeoutMsecs, int expireMsecs) {        this.lockKey = lockKey;        this.jedis = pool.getResource();        this.timeoutMsecs = timeoutMsecs;        this.expireMsecs = expireMsecs;        this.jedisLock = new JedisLock(jedis, lockKey.intern(), timeoutMsecs, expireMsecs);    }    public void wrap(Runnable runnable) {        long begin = System.currentTimeMillis();        try {            // timeout超时,等待入锁的时间,设置为3秒;expiration过期,锁存在的时间设置为5分钟            logger.info("begin logck,lockKey={},timeoutMsecs={},expireMsecs={}", lockKey, timeoutMsecs, expireMsecs);            if (jedisLock.acquire()) { // 启用锁                runnable.run();            } else {                //logger.info("The time wait for lock more than [{}] ms ", timeoutMsecs);            }        } catch (Throwable t) {            // 分布式锁异常            logger.warn(t.getMessage(), t);        } finally {            this.lockRelease(jedisLock, jedis);        }        logger.info("[{}]cost={}", lockKey, System.currentTimeMillis() - begin);    }    /**     * 释放锁,后期欲将离线计算的释放锁封装     *      * @param lock     * @param jedis     * @author come from network     * @date 2014-3-6     */    private void lockRelease(JedisLock lock,                             Jedis jedis) {        if (lock != null) {            try {                lock.release();// 则解锁            } catch (Exception e) {            }        }        if (jedis != null) {            try {                pool.returnResource(jedis);// 还到连接池里            } catch (Exception e) {            }        }        logger.info("release logck,lockKey={},timeoutMsecs={},expireMsecs={}", lockKey, timeoutMsecs, expireMsecs);    }    public static JedisPool getPool()    {        return pool;    }    public static synchronized void setPool(JedisPool pool)    {        SimpleLock.pool = pool;    }}

分布式锁测试

package com.chinaso.phl.concurrent;import org.apache.commons.pool2.impl.GenericObjectPoolConfig;import redis.clients.jedis.JedisPool;/** * @author com from network * @date   2014-3-13*/public class SimpleLockTest {    /**     * @param args     * @author com from network     * @date   2014-3-13    */    public static void main(String[] args) {        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();        final JedisPool pool = new JedisPool(poolConfig, "192.168.142.237", 6379, 3000); //最后一个参数为密码        SimpleLock.setPool(pool);//只需要初始化一次        String key = "test";        SimpleLock lock = new SimpleLock(key);        lock.wrap(new Runnable() {            @Override            public void run() {                //此处代码是锁上的                System.out.println(111);            }        });    }}