redis分布式锁实现控制并发

来源:互联网 发布:eggnog数据库 编辑:程序博客网 时间:2024/06/06 18:00

最近项目有一个关于征信协议生成pdf,要求客户签订的第一份协议生成pdf,由于是服务器集群,所以要用到分布式锁的概念。

写完后,写了个例子总结了下,直接上例子

import java.util.HashSet;

import java.util.Set;
import java.util.concurrent.TimeUnit;


import org.apache.commons.lang.math.RandomUtils;
import org.springframework.data.redis.core.TimeoutUtils;


import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;




public class Test {
    
    static Set<HostAndPort> haps_dest_dev = new HashSet<HostAndPort>();
    static JedisCluster jedisCluster;


    static {
        haps_dest_dev.add(new HostAndPort("172.29.0.97", 6379));
        haps_dest_dev.add(new HostAndPort("172.29.0.97", 6389));
        haps_dest_dev.add(new HostAndPort("172.29.0.97", 6399));
        haps_dest_dev.add(new HostAndPort("172.29.0.98", 6379));
        haps_dest_dev.add(new HostAndPort("172.29.0.98", 6389));
        haps_dest_dev.add(new HostAndPort("172.29.0.98", 6399));


        jedisCluster = new JedisCluster(haps_dest_dev);
    }
    
    /** 毫秒与毫微秒的换算单位 1毫秒 = 1000000毫微秒 */
    public static final long MILLI_NANO_CONVERSION = 1000 * 1000L;
    /** 默认超时时间(毫秒) */
    public static final long DEFAULT_TIME_OUT = 1000;
    
//    private volatile boolean locked = false;
    
    /** 
     * 获取锁 
     * @param timeout     获取锁的等待时间  毫秒
     * @param expireSecs  锁的有效时间,必须大于0 毫秒
     * @return 
     */  
    private boolean lock(String lockKey, long timeout, int expireSecs) throws InterruptedException {  
        if (expireSecs <= 0) {  
            throw new IllegalArgumentException("Param expireSecs must lager than zero.");  
        }  
        long nano = System.nanoTime();  
        timeout *= MILLI_NANO_CONVERSION;  
        try {  
            while ((System.nanoTime() - nano) < timeout) {  
            long expires = System.currentTimeMillis() + expireSecs + 1;
            // 5秒之后锁到期
                String expiresStr = String.valueOf(expires); //锁到期时间
                // 获取到锁
                if (jedisCluster.setnx(lockKey, String.valueOf(expiresStr)) == 1) {
                // lock acquired
//                 locked = true;
                jedisCluster.expire(lockKey, (int) TimeoutUtils.toSeconds(expireSecs, TimeUnit.MILLISECONDS)); 
                    return true;  
                }  
                // 没有获取到锁
                String currentValueStr = jedisCluster.get(lockKey); 
                // expireMsecs(5秒)锁的有效期内无法进入if判断,如果锁超时了
                if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
                //判断是否为空,不为空的情况下,如果被其他线程设置了值,则第二个条件判断是过不去的
                    // lock is expired
                // 如果锁超时重新设置
                    String oldValueStr = jedisCluster.getSet(lockKey, expiresStr);
                    //获取上一个锁到期时间,并设置现在的锁到期时间,
                    //只有一个线程才能获取上一个线上的设置时间,因为jedis.getSet是同步的
                    // 值相同说明是同一个线程的操作,获取锁成功
                    if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
                    //防止误删(覆盖,因为key是相同的)了他人的锁——这里达不到效果,这里值会被覆盖,但是因为什么相差了很少的时间,所以可以接受
                    //如过这个时候,多个线程恰好都到了这里,但是只有一个线程的设置值和当前值相同,他才有权利获取锁
                        // lock acquired
//                     locked = true;
                    jedisCluster.expire(lockKey, (int) TimeoutUtils.toSeconds(expireSecs, TimeUnit.MILLISECONDS));
                        return true;
                    } else {
                        // 被其他线程抢先获取锁
//                        locked = false;
                    }
                }
               // 锁没有超时,继续等待
            }  
            
            Thread.sleep(3, RandomUtils.nextInt(500));  
        } catch (Exception e) {  
            throw new RuntimeException("Locking error", e);  
        } 
        return false;  
    }
    
    
    /**
     * 释放锁
     */
    public void releaseLock(String lockKey){
        try {
            long current = System.currentTimeMillis();  
            // 避免删除非自己获取得到的锁
            if (current < Long.valueOf(jedisCluster.get(lockKey)))
            jedisCluster.del(lockKey);
//                locked = false;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public static void main(String[] args) {
    Test t = new Test();
        try {
        if (t.lock("lock:aa", DEFAULT_TIME_OUT, 5000)) {
        System.out.println("获取锁");
        } else {
        System.out.println("未获取锁");
        }
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }
}
原创粉丝点击