redis 分布式锁 spring boot

import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSON;

@Component
public class RedisUtil {

    @Autowired
    private RedisTemplate<String, String> template;
    /**
     * 
     * @param key
     * @param value
     * @param timeOut 秒
     */
    public void setValue(final String key,Object value,long timeOut) {
        ValueOperations<String, String> ops = this.template.opsForValue();
        ops.set(key, JSON.toJSONString(value), timeOut, TimeUnit.SECONDS);
    }
    
    public void removeKey(final String key) {
        this.template.delete(key);
    }
    /**
     * 更新过期时间
     * @param key
     * @param timeOut (单位,秒)
     */
    public void expire(final String key,long timeOut) {
        template.expire(key, timeOut, TimeUnit.SECONDS);
    }

    
    public boolean getLock(String key) {
        String value = String.format("%s", System.currentTimeMillis()+90*1000);
        if(template.opsForValue().setIfAbsent(key, value)) {
            return true;
        }
        //currentValue=A   这两个线程的value都是B  其中一个线程拿到锁
        String currentValue = template.opsForValue().get(key);
        //如果锁过期
        if (!StringUtils.isEmpty(currentValue)
                && Long.parseLong(currentValue) < System.currentTimeMillis()) {
            //获取上一个锁的时间
            String oldValue = template.opsForValue().getAndSet(key, value);
            if (!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)) {
                return true;
            }
        }
        return false;
    }
    
    /**
     * 解锁
     * @param key
     * @param value
     */
    public void removeLock(String key) {
        template.opsForValue().getOperations().delete(key);
    }
    
    public <T> T getValue(final String key,Class<T> clz) {
        ValueOperations<String, String> ops = this.template.opsForValue();
        String jsonStr = ops.get(key);
        if (jsonStr != null) {
            T t = JSON.parseObject(jsonStr, clz);
            return t;
        }
        return null;
    }
    
    public boolean hasKey(String key){
        return template.hasKey(key);
    }

}

测试类

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;

import com.thgames.exception.BusinessException;
import com.thgames.proxy.config.RedisUtil;

@RunWith(SpringRunner.class)
@SpringBootTest(classes=GiftApplication.class)
@ActiveProfiles("dev")
public class GameProxyApplicationTests2 {

    @Autowired private RedisUtil redisUtil;
    
    @Test
    public void skipTest() throws BusinessException {
        
        for(int i=0;i<20;i++) {
            new Thread() {

                @Override
                public void run() {
                    testlock("uid-a", "prefix_a");
                }
                
            }.start();
        }
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
    }

    public void testlock(String uid,String prefix) {
        boolean flag = false;
        try {
            if(flag=redisUtil.getLock(uid+prefix)) {
                Thread.sleep(2000);
                System.out.println(Thread.currentThread().getName()+" getLock success");
            } else {
                System.out.println(Thread.currentThread().getName()+" not getLock");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(flag)redisUtil.removeLock(uid+prefix);
        }
    }
    
}

运行结果

Thread-5 not getLock
Thread-8 not getLock
Thread-4 not getLock
Thread-23 not getLock
Thread-14 not getLock
Thread-18 not getLock
Thread-7 not getLock
Thread-11 not getLock
Thread-15 not getLock
Thread-19 not getLock
Thread-10 not getLock
Thread-6 not getLock
Thread-22 not getLock
Thread-9 not getLock
Thread-17 not getLock
Thread-16 not getLock
Thread-12 not getLock
Thread-20 not getLock
Thread-13 not getLock
Thread-21 getLock success

原理,redis使用的是单线程模型,setnx命令和getset命令保证原子性,线程安全,低版本setnx没有过期时间参数,为了避免死锁,所以得把过期时间当作value传过去,然后再用getset返回的旧值与当前值比较,相等则获得锁,类似CAS的原理

猜你喜欢

转载自www.cnblogs.com/xiongjinpeng/p/12938362.html