Redis——实现悲观锁

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lhc2207221755/article/details/80836006

抽象类:

/**
 * \* Created: liuhuichao
 * \* Date: 2018/6/5
 * \* Time: 上午10:35
 * \* Description: redis 锁抽象类
 * \
 */
public abstract class AbstractLock implements Lock{

    protected volatile boolean locked;

    private Thread exclusiveOwnerThread;

    @Override
    public void lock() {
        try {
            lock(false, 0, null, false);
        } catch (InterruptedException e) {

        }
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {
        lock(false, 0, null, true);
    }

    public boolean tryLockInterruptibly(long time, TimeUnit unit) throws InterruptedException {
        return lock(true, time, unit, true);
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit)  {
        try {
            System.out.println("try--lock");
            return lock(true, time, unit, false);
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("" + e);
        }
        return false;
    }

    @Override
    public void unlock() {
        // 检查当前线程是否持有锁
        if (Thread.currentThread() != getExclusiveOwnerThread()) {
            throw new IllegalMonitorStateException("current thread does not hold the lock");
        }
        unlock0();
        setExclusiveOwnerThread(null);
    }

    protected void setExclusiveOwnerThread(Thread thread) {
        exclusiveOwnerThread = thread;
    }

    protected final Thread getExclusiveOwnerThread() {
        return exclusiveOwnerThread;
    }

    protected abstract void unlock0();

    /**
     * 阻塞式获取锁的实现
     * @param useTimeout
     * @param time
     * @param unit
     * @param interrupt 是否响应中断
     * @return
     * @throws InterruptedException
     */
    protected abstract boolean lock(boolean useTimeout, long time, TimeUnit unit, boolean interrupt)
            throws InterruptedException;
}

实现类:

/**
 * \* Created: liuhuichao
 * \* Date: 2018/6/5
 * \* Time: 下午4:09
 * \* Description:基于Redis的SETNX操作实现的分布式锁基于Redis的SETNX操作实现的分布式锁
 *          获取锁时最好用lock(long time, TimeUnit unit), 以免网路问题而导致线程一直阻塞
 *
 *       SETNX命令(SET if Not eXists)
             语法:
             SETNX key value
             功能:
             当且仅当 key 不存在,将 key 的值设为 value ,并返回1;若给定的 key 已经存在,则 SETNX 不做任何动作,并返回0。

         GETSET命令(这是一个原子命令!)
             语法:
             GETSET key value
             功能:
             将给定 key 的值设为 value ,并返回 key 的旧值 (old value),当 key 存在但不是字符串类型时,返回一个错误,当key不存在时,返回nil。
 * \
 */
public class RedisBasedOnSetNxLock extends AbstractLock{


    private Jedis jedis;

    // 锁的名字
    protected String lockKey;

    // 锁的有效时长(毫秒)
    protected long lockExpires;

    public RedisBasedOnSetNxLock(Jedis jedis, String lockKey, long lockExpires) {
        this.jedis = jedis;
        this.lockKey = lockKey;
        this.lockExpires = lockExpires;
    }

    @Override
    protected void unlock0() {

    }

    @Override
    protected boolean lock(boolean useTimeout, long time, TimeUnit unit, boolean interrupt) throws InterruptedException {
        if(interrupt){
            checkInterruption();
        }
        long start = System.currentTimeMillis();
        long timeout = unit.toMillis(time);
        while (useTimeout ? isTimeout(start, timeout) : true) {
            if (interrupt) {
                checkInterruption();
            }
            long lockExpireTime = System.currentTimeMillis() + lockExpires + 1;// 锁超时时间
            String stringOfLockExpireTime = String.valueOf(lockExpireTime);
            if (jedis.setnx(lockKey, stringOfLockExpireTime) == 1) { // 获取到锁
                //成功获取到锁, 设置相关标识
                locked = true;
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            String value = jedis.get(lockKey);
            if (value != null && isTimeExpired(value)) { // lock is expired
                // 假设多个线程(非单jvm)同时走到这里
                String oldValue = jedis.getSet(lockKey, stringOfLockExpireTime); //原子操作
                /**
                 * 但是走到这里时每个线程拿到的oldValue肯定不可能一样(因为getset是原子性的)
                 * 假如拿到的oldValue依然是expired的,那么就说明拿到锁了
                 */
                if (oldValue != null && isTimeExpired(oldValue)) {
                    //成功获取到锁, 设置相关标识
                    locked = true;
                    setExclusiveOwnerThread(Thread.currentThread());
                    return true;
                }
            } else {
                // TODO lock is not expired, enter next loop retrying
            }
        }
        return false;
    }

    public boolean tryLock() {
        long lockExpireTime = System.currentTimeMillis() + lockExpires + 1;// 锁超时时间
        String stringOfLockExpireTime = String.valueOf(lockExpireTime);

        if (jedis.setnx(lockKey, stringOfLockExpireTime) == 1) { // 获取到锁
            // 成功获取到锁, 设置相关标识
            locked = true;
            setExclusiveOwnerThread(Thread.currentThread());
            return true;
        }

        String value = jedis.get(lockKey);
        if (value != null && isTimeExpired(value)) { // lock is expired
            // 假设多个线程(非单jvm)同时走到这里
            String oldValue = jedis.getSet(lockKey, stringOfLockExpireTime); //原子操作
            // 但是走到这里时每个线程拿到的oldValue肯定不可能一样(因为getset是原子性的)
            // 假如拿到的oldValue依然是expired的,那么就说明拿到锁了
            if (oldValue != null && isTimeExpired(oldValue)) {
                //成功获取到锁, 设置相关标识
                locked = true;
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
        } else {
            // TODO lock is not expired, enter next loop retrying
        }
        return false;
    }



    private void checkInterruption() throws InterruptedException {
        if (Thread.currentThread().isInterrupted()) {
            throw new InterruptedException();
        }
    }

    private boolean isTimeExpired(String value) {
        return Long.parseLong(value) < System.currentTimeMillis();
    }

    /**
     * judge whether timeout or not
     * @param start
     * @param timeout
     * @return if timeout return true; else false
     */
    private boolean isTimeout(long start, long timeout) {
        return start + timeout > System.currentTimeMillis();
    }

    private void doUnlock() {
        jedis.del(lockKey);
    }

    @Override
    public Condition newCondition() {
        return null;
    }
}

猜你喜欢

转载自blog.csdn.net/lhc2207221755/article/details/80836006