分布式锁

1. 基于redis的分布式锁,使用Jedis客户端

SET

SET key value [EX seconds] [PX milliseconds] [NX|XX]

  • EX second :设置键的过期时间为 second 秒。 SET key value EX second 效果等同于 SETEX key second value 。
  • PX millisecond :设置键的过期时间为 millisecond 毫秒。 SET key value PX millisecond 效果等同于 PSETEX key millisecond value 。
  • NX :只在键不存在时,才对键进行设置操作。 SET key value NX 效果等同于 SETNX key value 。
  • XX :只在键已经存在时,才对键进行设置操作。

非阻塞锁

@Component
public class RedisDistributedLock1 {

    @Autowired
    private JedisCluster jedisCluster;

    private int exprieTime = 10;

    private String prefix ="lock:";
    
    private String defaultVal = "default";

    /**
     * 获取锁:
     * 获取到锁后设置过期时间返回 true
     * @param lockKey
     * @return
     */
    public boolean lock(String lockKey){
        String defaultKey = prefix+lockKey;
        String result = jedisCluster.set(defaultKey, defaultVal,"NX", "EX", exprieTime);
        if("OK".equals(result){
            return true;
        }
        return false;
    }

    /**
     * 释放锁:直接删除key
     * @param lockKey
     */
    public void unlock(String lockKey){
        jedisCluster.del(lockKey);
    }
}

 

阻塞锁:

@Component
public class RedisDistributedLock2 {

    //redis缓存
    @Autowired
    private JedisCluster jedisCluster;
    //锁超时时间 防止死锁(时间不能太短,防止正常业务过程执行慢 而导致超时)(单位是秒)
    private static final int TIME_OUT = 60;
    //获取不到锁的等待重试时间(毫秒)
    private static final int RETRY_TIME = 100;

    public void acquire(String key) {
        String lockVal = UUID.randomUUID().toString();
        while(true){
            String result = jedisCluster.set(defaultKey, defaultVal,"NX", "EX", exprieTime);
            if(!"OK".equals(result){
                //当客户端无法获取锁时,它应该在一个随机延迟后重试,从而避免多个客户端同时试图获取锁
                try {
                    Thread.sleep(RETRY_TIME + (int)(Math.random() * RETRY_TIME));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else{
                //获取锁成功, 跳槽循环。
                break;
            }
        }

    }

    public void release(String key) {
        jedisCluster.del(key);
    }
}

 

2.基于zookeeper的分布式锁

 

思路:利用名称唯一性,加锁操作时,只需要所有客户端一起创建lockPath节点,只有一个创建成功,成功者获得锁。解锁时,只需删除lockPath节点,其余客户端再次进入竞争创建节点,直到所有客户端都获得锁。

保证锁释放: 客户端会话结束时,zookeeper会将该ephemeral znode删除,ephemeral znode不可以有子节点。

public class ZkDistributeLock1 {

    private ZkClient client;
    private long wait_interval = 50;


    public void acquire(String lockPath) throws Exception {

        if (attemptLock(lockPath, -1) == null ) {
            throw new IOException("连接丢失!在路径:'"+lockPath+"'下不能获取锁!");
        }
    }

    public boolean acquire(String lockPath, long millisecond) throws Exception {

        return attemptLock(lockPath, millisecond) != null;
    }

    public void release(String lockPath) {
        client.delete(lockPath);
    }

    /**
     * 尝试获取锁 ,直到获取成功或者超时。
     * @param millisecond
     * @return
     * @throws Exception
     */
    private String attemptLock(String lockPath, long millisecond) throws Exception{
        long  startMillis = System.currentTimeMillis();
        Long  millisToWait = (millisecond < 0) ? null :millisecond;

        String returnPath = null;
        while(returnPath == null){
            try {
                //如果客户端宕机, zookeeper会自动释放锁, 防止死锁。
                returnPath = client.create(lockPath,null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL);
            } catch (Exception e) {
                Thread.sleep(wait_interval);
            }
            if(millisToWait != null && returnPath == null){
                millisToWait -= (System.currentTimeMillis() - startMillis);
                startMillis = System.currentTimeMillis();
                if ( millisToWait <= 0 )  break;
            }
        }
        return returnPath;
    }
}

 

3. 基于TAIR的版本号的分布式锁

Tair中的每个数据都包含版本号,版本号在每次更新后都会递增。插入的时候初始版本号1,更新的时候需要比较版本号是否一致,一致才能更新成功,不一致就会更新失败。

分布式锁给定一个比较大的版本号LOCK_VERSION,比如Integer.MAX_VALUE, 这样只有第一次PUT可以成功,更新数据都会失败。

/**
     * 分布式锁,加锁
     *
     * @param key
     * @param expireTime
     * @return
     */
    public boolean lock(String key, int expireTime) {
        ResultCode rc = tairManager.put(namespace, key, 0, LOCK_VERSION, expireTime);
        if (ResultCode.SUCCESS.equals(rc)) {
            return true;
        }  else {
            return false;
        }
    }

 /**
     * 分布式锁,释放锁
     *
     * @param key
     * @return
     */

public boolean unlock(String key){
        ResultCode rc = tairManager.delete(namespace, key);
        if (ResultCode.SUCCESS.equals(rc)
                || ResultCode.DATANOTEXSITS.equals(rc)) {
           
            return true;
        } else {
           
            return false;
        }
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值