《redis in action》——锁

2018-06-07  本文已影响0人  墨荒

redis测试项目搭建

新建项目

http://start.spring.io/填写项目标识、添加redis依赖,下载项目用idea打开

配置redis

spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接超时时间(毫秒)
spring.redis.timeout=30000
public class RedisConnectTest extends RedisApplicationTests{
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Test
    public void testRedis() throws Exception {
        stringRedisTemplate.opsForValue().set("a", String.valueOf(1));
        System.out.println(stringRedisTemplate.opsForValue().get("a"));
    }
}

场景

定义锁的工具类接口

public interface LockHelper {
    void lock(String key);
    void unLock(String key);
}

调用的时候注意在finally的时候释放锁,避免业务执行失败时没释放所导致死锁

            try {
                lockHelper.lock(account.id);
                super.run();
            }finally {
                lockHelper.unLock(account.id);
            }

本地锁

ConcurrentHashMap+ReentrantLock:

@Component("LocalLockHelper")
public class LocalLockHelper implements LockHelper {
    private ConcurrentHashMap<String,Lock> lockMap = new ConcurrentHashMap<>();
    @Override
    public void lock(String key) {
        Lock lock = lockMap.get(key);
        if(lock == null){
            Lock newLock = new ReentrantLock();
            Lock existLock = lockMap.putIfAbsent(key,newLock);
            //锁不存在的话就用新锁,存在的话用旧锁
            lock = existLock == null?newLock:existLock;
        }
        lock.lock();
    }
    @Override
    public void unLock(String key) {
        Lock lock = lockMap.get(key);
        if(lock != null){
            lock.unlock();
        }
    }
}

redis锁

加锁多了个支持锁过期的方法,防止分布式情况下一些客户端没有(成功)释放锁,甚至在第一次申请锁的时候对应的key已经存在。

@Component("RedisLockHelper")
public class RedisLockHelper implements LockHelper,ExpireLock{
    @Autowired
    private StringRedisTemplate redisTemplate;
    private Map<String, String> keyMap = new HashMap<>();

    @Override
    public void lock(String key) {
        BoundValueOperations<String,String> boundValueOperations =
                this.redisTemplate.boundValueOps(getLockKey(key));
        while (true) {
            if (boundValueOperations.setIfAbsent(this.getRequestId())) {
                break;
            }else {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     *设置持有锁的超时时间,防止某些客户端未释放锁
     */
    @Override
    public void lock(String key, long expireTimeMills) {
        BoundValueOperations<String,String> boundValueOperations =
                this.redisTemplate.boundValueOps(getLockKey(key));
        while (true) {
            if (boundValueOperations.setIfAbsent(this.getRequestId())) {
                boundValueOperations.expire(expireTimeMills, TimeUnit.MILLISECONDS);
                break;
            }else {
                long wait;
                if (boundValueOperations.getExpire() == -1) {
                    System.out.println(key+"|"+Thread.currentThread().getName()+"设置锁超时时长");
                    //防止未释放锁
                    wait = expireTimeMills;
                    boundValueOperations.expire(expireTimeMills, TimeUnit.MILLISECONDS);
                }else{
                    wait = 100;
                }
                try {
                    Thread.sleep(wait);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void unLock(String key) {
        String lk = getLockKey(key);
        BoundValueOperations<String,String> boundValueOperations =
                this.redisTemplate.boundValueOps(lk);
        if(this.getRequestId().equals(boundValueOperations.get())) {
//            释放锁的时候检测当前值是否为请求id,防止被其他请求释放,这个地方应该用lua脚本 把检查和删除 做成一个有原子性的方法或者命令
            redisTemplate.delete(lk);
        }
    }

    private String getRequestId() {
        //多实例情况下需要设置为http requestId之类的
        return String.valueOf(Thread.currentThread().getId());
    }

    private String getLockKey(String key) {
        String t = keyMap.get(key);
        if(t == null){
            t = "lock:"+key;
            keyMap.put(key,t);
        }
        return t;
    }
}
上一篇下一篇

猜你喜欢

热点阅读