redis分布式锁的实现

第一步:分布式锁实现类:

Java代码 收藏代码

import redis.clients.jedis.ShardedJedis;  
  
import com.suning.framework.sedis.ShardedJedisAction;  
import com.suning.framework.sedis.impl.ShardedJedisClientImpl;  
  
/** 
 * 基于redis实现的分布式锁 
 *  
 * @author guweiqiang 
 */  
public class DistributedSedisLock {  
  
    private ShardedJedisClientImpl jedisClient; // jedis client  
    private String lockKey; // 锁的redis key  
    private int expireMsecs = 60 * 1000; // 锁超时,防止线程在入锁以后,无限的执行等待  
    private int timeoutMsecs = 10 * 1000; // 锁等待,防止线程饥饿  
    private boolean locked = false;// 拿到锁的标示:true表示拿到了锁  
      
    private static final long DEFAULT_SLEEP_TIME = 100; // 线程睡眠时间100毫秒  
  
    /*********************构造方法 start************************************/  
    public DistributedSedisLock(ShardedJedisClientImpl jedisClient,  
            String lockKey) {  
        this.jedisClient = jedisClient;  
        this.lockKey = lockKey;  
    }  
  
    public DistributedSedisLock(ShardedJedisClientImpl jedisClient,  
            String lockKey, int timeoutMsecs) {  
        this(jedisClient, lockKey);  
        this.timeoutMsecs = timeoutMsecs;  
    }  
  
    public DistributedSedisLock(ShardedJedisClientImpl jedisClient,  
            String lockKey, int timeoutMsecs, int expireMsecs) {  
        this(jedisClient, lockKey, timeoutMsecs);  
        this.expireMsecs = expireMsecs;  
    }  
    /*********************构造方法 end************************************/  
      
    public String getLockKey() {  
        return lockKey;  
    }  
      
    /** 
     * 判断是否拿到了锁(对外提供的获取锁的方法) 
     * @return true:拿到了锁;false:没有拿到锁 
     * @throws InterruptedException 
     */  
    public synchronized boolean acquire() throws InterruptedException {  
        return acquire(jedisClient);  
    }  
      
    /** 
     * 判断是否拿到了锁 
     * @param redisClient 
     * @return true:拿到了锁;false:没有拿到锁 
     * @throws InterruptedException 
     */  
    private synchronized boolean acquire(ShardedJedisClientImpl jedisClient) throws InterruptedException {  
        int timeout = timeoutMsecs;  
        while (timeout >= 0) {  
            long expires = System.currentTimeMillis() + expireMsecs + 1;  
            final String expiresStr = String.valueOf(expires); // 锁到期时间  
            // 加锁  
            Long setnxResult = jedisClient.execute(new ShardedJedisAction<Long>() {  
                public Long doAction(ShardedJedis jedis) {  
                    return jedis.setnx(lockKey, expiresStr);  
                }  
            });  
            if (setnxResult!=null && setnxResult.intValue()==1) { // setnx返回1,表示设置成功  
                // lock acquired success  
                locked = true;  
                return true;  
            }  
              
            // 获取redis里的时间  
            String currentValueStr = jedisClient.execute(new ShardedJedisAction<String>() {  
                public String doAction(ShardedJedis jedis) {  
                    return jedis.get(lockKey);  
                }  
            });  
            if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {  
                // 判断是否为空,不为空的情况下,如果被其他线程设置了值,则第二个条件判断是过不去的  
                // lock is expired  
                String oldValueStr = jedisClient.execute(new ShardedJedisAction<String>() {  
                    public String doAction(ShardedJedis jedis) {  
                        return jedis.getSet(lockKey, expiresStr);  
                    }  
                });  
  
                // 获取上一个锁到期时间,并设置现在的锁到期时间,  
                // 只有一个线程才能获取上一个线程的设置时间,因为jedis.getSet是同步的(原子的)  
                if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {  
                    // 如过这个时候,多个线程恰好都到了这里,但是只有一个线程的设置值和当前值相同,他才有权利获取锁  
                    // lock acquired  
                    locked = true;  
                    return true;  
                }  
            }  
              
            timeout -= DEFAULT_SLEEP_TIME;  
            Thread.sleep(DEFAULT_SLEEP_TIME);  
        }  
          
        return false;  
    }  
  
    /** 
     * 释放锁(对外提供的释放锁的方法) 
     */  
    public synchronized void release() {  
        release(jedisClient);  
    }  
      
    /** 
     * 释放锁 
     */  
    private synchronized void release(ShardedJedisClientImpl jedisClient) {  
        if (locked) {  
            jedisClient.execute(new ShardedJedisAction<Long>() {  
                public Long doAction(ShardedJedis jedis) {  
                    return jedis.del(lockKey);  
                }  
            });  
            locked = false;  
        }  
    }  
}  
 

第二步:对外暴露的使用工具类:

Java代码 收藏代码

import org.slf4j.Logger;  
import org.slf4j.LoggerFactory;  
  
import com.suning.framework.sedis.impl.ShardedJedisClientImpl;  
  
/** 
 * 分布式锁使用工具类 
 *  
 * @author guweiqiang 
 */  
public class DistributedLock {  
      
    private static final Logger LOGGER = LoggerFactory.getLogger(DistributedLock.class.getName());  
  
    private DistributedSedisLock distributedSedisLock; // 分布式锁  
    private static ShardedJedisClientImpl jedisClient; // jedis client  
    private String lockKey; // 锁的redis key  
    private int expireMsecs; // 锁超时,防止线程在入锁以后,无限的执行等待  
    private int timeoutMsecs; // 锁等待,防止线程饥饿  
      
    public DistributedLock(String lockKey){  
        this(lockKey, 3000, 300000);  
    }  
      
    public DistributedLock(String lockKey, int timeoutMsecs, int expireMsecs){  
        this.lockKey = "YEB:BYUTIL:SHP:LOCK:" + lockKey;  
        this.timeoutMsecs = timeoutMsecs;  
        this.expireMsecs = expireMsecs;  
        this.distributedSedisLock = new DistributedSedisLock(jedisClient, this.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(distributedSedisLock.acquire()){ // 拿到了锁,执行线程任务  
                runnable.run();  
            } else {  
                LOGGER.info("The time wait for lock more than [{}] ms ", timeoutMsecs);  
            }  
        } catch(Exception e){  
            LOGGER.error("acquire lock Exception ", e);  
        } finally {  
            LOGGER.info("[{}]cost={}", lockKey, System.currentTimeMillis() - begin);  
            // 释放锁  
            if(distributedSedisLock!=null){  
                distributedSedisLock.release();  
            }  
        }  
    }  
      
    /** 
     * 初始化jedisClient 
     * @param jedisClient 
     */  
    public static synchronized void setShardedJedisClient(ShardedJedisClientImpl jedisClient) {  
        DistributedLock.jedisClient = jedisClient;  
    }  
}  

在配置一个监听器,用来初始化jedis client(使用其他方式进行初始化也可以):

Java代码 收藏代码

import javax.servlet.ServletContextEvent;  
  
import org.springframework.context.ApplicationContext;  
import org.springframework.web.context.ContextLoaderListener;  
  
import com.suning.framework.sedis.impl.ShardedJedisClientImpl;  
import com.suning.shp.utils.DistributedLock;  
  
/** 
 * 启动监听器 
 *  
 * @author guweiqiang 
 */  
public class SystemListener extends ContextLoaderListener {  
  
    private ApplicationContext applicationContext;  
  
    @Override  
    public void contextInitialized(ServletContextEvent event) {  
        super.contextInitialized(event);  
          
        /************* spring *********/  
        applicationContext = super.getCurrentWebApplicationContext();  
  
        /**** redis 分布式锁 *****/  
        DistributedLock.setShardedJedisClient(applicationContext.getBean("jedisClient", ShardedJedisClientImpl.class));  
    }  
  
    @Override  
    public void contextDestroyed(ServletContextEvent event) {  
        super.contextDestroyed(event);  
    }  
  
}  

监听器写好之后,需要在web.xml里配置一下:

Xml代码 收藏代码

<listener>  
    <listener-class>com.suning.shp.listener.SystemListener</listener-class>  
</listener> 

至此,一个基于redis实现的分布式锁就可以使用了,使用方法如下:

Java代码 收藏代码

DistributedLock lock = new DistributedLock(key, 10000, 5000);  
try {  
          lock.wrap(new Runnable(){  
               @Override  
               public void run() {  
                   // 这里写需要加分布式锁的业务代码  
                }  
          });  
} catch (Exception e){  
         LOGGER.error("发生异常:" + e.getMessage(), e);  
}  

转自
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值