分布式方法锁

注意:单个线程任务处理时间threadTime、获取锁的等待时间acquireTimeout、锁的过期时间timeout三者之间的关系:

1、threadTime < timeout  (不然会出错)
2、acquireTimeout < threadTime < timeout  (一般标准的设置时间方法)

废话不多说,直接上代码:

TestRedisLock类

public class TestRedisLock {

    //redis分布式锁
    public static void lock(){
        String indentifier = null;
        RedisLock redisLock = null;
        try {
            //toDO 增加分布式锁逻辑
            redisLock = new RedisLock(RedisClient.jedisSentinelPool);
            //首次获取到锁是必然的  acquireTimeout 为获取锁的等待时间,如果超过此时间就放弃      timeout 为锁的过期时间
            indentifier = redisLock.lockWithTimeout("ss_test",500, 3000);
            System.out.println(Thread.currentThread()+"尝试获取锁,indentifier:"+indentifier);
            ThreadLocal<AtomicInteger> threadLocal = new ThreadLocal();
            threadLocal.set(new AtomicInteger(0));
            while (indentifier == null){//一直请求锁,直到拿到为止
                threadLocal.get().getAndIncrement();
                indentifier = redisLock.lockWithTimeout("ss_test",500, 3000);
                //Thread.currentThread().sleep(100);
                System.out.println(Thread.currentThread()+"尝试获取锁,indentifier:"+indentifier+",第"+threadLocal.get().get()+"次");
            }
            if(indentifier!=null){
                //拿到锁开始干活
                System.out.println(Thread.currentThread()+"获取到锁,indentifier:"+indentifier);
                Thread.currentThread().sleep(2000);
                //System.out.println(1/0);
                //todo 执行任务
                System.out.println(Thread.currentThread()+"执行完成");
            }else {
                System.out.println(Thread.currentThread()+"没有获取到锁");
            }
        } catch(Exception ex){
            ex.printStackTrace();
        } finally {
            if(redisLock!=null && StringUtils.isNotEmpty(indentifier)){
                redisLock.releaseLock("ss_test",indentifier);
                System.out.println(Thread.currentThread()+"释放锁");
            }
        }
    }



    //todo 线程锁
    public static void main(String[] args) {
        //模拟100人抢购
        for (int i=0;i<5;i++){
            Thread thread = new Thread(){
                    @Override
                    public void run(){
                        lock();
                    }
            };
            thread.start();
        }
    }

}

RedisLock类

public class RedisLock {
    private final JedisPool jedisPool;
    private static final Logger logger = Logger.getLogger(RedisLock.class);

    public RedisLock(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    /**
     *
     * @param locaName  锁的key
     * @param acquireTimeout  尝试获取锁超时时间
     * @param timeout   key的超时时间
     * @return 锁标识
     */
    public String lockWithTimeout(String locaName,
                                  long acquireTimeout, long timeout) {
        Jedis conn = null;
        String retIdentifier = null;
        try {
            // 获取连接
            // 随机生成一个value
            String identifier = UUID.randomUUID().toString();    //如果identifier存在则成功获取到锁,否则表明该锁已经被其他线程占用
            // 锁名,即key值
            String lockKey = "lock:" + locaName;
            // 超时时间,上锁后超过此时间则自动释放锁
            int lockExpire = (int)(timeout / 1000);
            conn = jedisPool.getResource();
            // 获取锁的超时时间,超过这个时间则放弃获取锁
            long end = System.currentTimeMillis() + acquireTimeout;
            while (System.currentTimeMillis() < end) {
                //Jedis conn = jedisPool.getResource();
                //System.out.println("等待时间:"+System.currentTimeMillis());
                if (conn.setnx(lockKey, identifier) == 1) {
                    conn.expire(lockKey, lockExpire);
                    // 返回value值,用于释放锁时间确认
                    retIdentifier = identifier;
                    /*if (conn != null) {
                        conn.close();
                        //System.out.println("关闭连接");
                    }*/
                    return retIdentifier;
                }
                // 返回-1代表key没有设置超时时间,为key设置一个超时时间
                if (conn.ttl(lockKey) == -1) {
                    conn.expire(lockKey, lockExpire);
                }

                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    logger.error("RedisLock中断异常",e);
                }
             /*   if (conn != null) {
                    conn.close();
                    //System.out.println("关闭连接");
                }*/
            }
        } catch (JedisException e) {
            logger.error("RedisLock获取锁异常",e);
            //jedisPool.returnBrokenResource(conn);
        } finally {
            if (conn != null) {
                conn.close();
               // System.out.println("关闭连接");
            }
        }
        return retIdentifier;
    }

    /**
     * 释放锁
     * @param lockName 锁的key
     * @param identifier    释放锁的标识
     * @return
     */
    public boolean releaseLock(String lockName, String identifier) {
        Jedis conn = null;
        String lockKey = "lock:" + lockName;
        boolean retFlag = false;
        try {
            conn = jedisPool.getResource();
            while (true) {
                // 监视lock,准备开始事务
                conn.watch(lockKey);
                // 通过前面返回的value值判断是不是该锁,若是该锁,则删除,释放锁
                if (identifier.equals(conn.get(lockKey))) {
                    Transaction transaction = conn.multi();
                    transaction.del(lockKey);
                    List<Object> results = transaction.exec();
                    if (results == null) {
                        continue;
                    }
                    retFlag = true;
                }
                conn.unwatch();
                break;
            }
        } catch (JedisException e) {
            logger.error("RedisLock释放锁异常",e);
        } finally {
            if (conn != null) {
                conn.close();
            }
        }
        return retFlag;
    }
}

本次测试模拟五个线程,acquireTimeout = 2500, threadTime = 2700 , timeout = 3000

猜你喜欢

转载自blog.csdn.net/zhanglixin999/article/details/121565723
今日推荐