redis锁

使用redis锁进行每个订单顺序执行

再对订单编号进行redis锁的时候,发现有些订单报错:等待中断

场景:

RLock redisSyncLock = redisson.getLock(billRepayPlanDto.getSysSeqId());
if (redisSyncLock.tryLock(6000, 60, TimeUnit.SECONDS)) {
    // 业务处理
}

查看tryLock处的源码,
[外链图片转存失败(img-xWbPlRb6-1569304434561)(./_image/2019-08-08/2019-08-08-14-28-26.png)]
设置等待时间为waitTime毫秒,则是等待时间为6秒,如果超过了六秒,则就获取锁失败。

大多数认为的写法

RLock lock = redisson.getLock(KEY);
lock.lock()
// do your own work
lock.unlock()

简单看完源代码后, 我看到该方法会去调用一个响应一个中断的lockInterruptibly,此时我就有点疑惑了, 响应中断就是表示线程如果发生中断就不会在等待队列中等待(当然redisson是采用SUB/PUB的方式),(本文不分析源码哈,对该锁的源码分析会放到专门博客里面分析, 主要是验证该如何使用)可以看下图:
[外链图片转存失败(img-HVPCdIgH-1569304434562)(./_image/2019-08-08/2019-08-08-15-40-43.jpg)]
上图中lock等方法会最终调用public void lockInterruptibly(long leaseTime, TimeUnit unit) throws InterruptedException 该方法会抛出异常, 然而lock方法并没有把这个异常抛出给使用者, 而是采用捕获异常,并且重新设置中断状态.
这下就有点明白了, 是不是需要用户自己来判断当前线程的状态来判断当前线程是否获得锁了呢?已经猜到这一步了, 接下来就需要验证一下自己的猜想

例子1:验证上面的写法

依赖:


    org.redisson
    redisson
    2.7.0

demo:

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.config.Config;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class TestDistributedRedisLock {

    private static CountDownLatch finish = new CountDownLatch(2);
    private static final String KEY = "testlock";
    private static Config config;
    private static Redisson redisson;
    static {
        config = new Config();
        config.useSingleServer().setAddress("127.0.0.1:6379");
        redisson = (Redisson)Redisson.create(config);
    }

    public static void main(String[] args) {
        Thread thread_1 = new LockWithoutBoolean("thread-1");
        Thread thread_2 = new LockWithoutBoolean("thread-2");
        thread_1.start();
        try {
            TimeUnit.SECONDS.sleep(10); // 睡10秒钟 为了让thread_1充分运行
            thread_2.start();
            TimeUnit.SECONDS.sleep(10); // 让thread_2 等待锁
            thread_2.interrupt(); // 中断正在等待锁的thread_2 观察thread_2是否会不会拿到锁
            finish.await();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            redisson.shutdown();
        }
    }

    static class LockWithoutBoolean extends Thread {
        private String name;
        public LockWithoutBoolean(String name) {
            super(name);
        }
        public void run() {
            RLock lock = redisson.getLock(KEY);
            lock.lock(10, TimeUnit.MINUTES);
            System.out.println(Thread.currentThread().getName() + " gets lock. and interrupt: " + Thread.currentThread().isInterrupted());
            try {
                TimeUnit.MINUTES.sleep(1);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                try {
                    lock.unlock();
                } finally {
                    finish.countDown();
                }
            }
            System.out.println(Thread.currentThread().getName() + " ends.");
        }
    }
}

在该例子中我启动了两个线程分别为thread-1和thread-2, 并且让线程thread-1获得锁后休息1分钟, 线程thread-2在等待锁的过程中用主线程中断线程thread-2以此达到测试的目的.
接下来就需要观察结果, 在线程thread-2中断的时候会不会获得锁, 如何观察呢? 因为我们知道如果一个线程尝试去释放一个属于别的线程的锁的时候, 会抛出一个运行时异常叫做异常, 另外我们也可以通过观察redis里面数据的变化情况来判断thread-2到底有没有获得锁.
运行结果:

thread-1 gets lock. and interrupt: false
thread-2 gets lock. and interrupt: true
Exception in thread "thread-2" java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id: 9f178836-f7e1-44fe-a89d-2db52f399c0d thread-id: 21
    at org.redisson.RedissonLock.unlock(RedissonLock.java:353)
    at com.example.TestDistributedRedisLock$LockWithoutBoolean.run(TestDistributedRedisLock.java:53)
thread-1 ends.

从程序的角度看线程thread-2有没有获得锁: 可以看到在thread-1还没有结束的时候,也就是在thread-1在获得锁但是还没有释放锁的时候, thread-2由于被别的线程中断停止了等待从lock.lock(10, TimeUnit.MINUTES)的阻塞状态中返回继续执行接下来的逻辑,并且由于尝试去释放一个属于线程thread-1的锁而抛出了一个运行时异常导致该线程thread-2结束了, 然而thread-2完成了一系列操作后,线程thread-1才释放了自己的锁. 所以thread-2并没有获得锁,却执行了需要同步的内容,还尝试去释放锁.
从redis的角度看线程thread-2有没有获得锁: 下图便是整个运行期间KEY中内容的变化,从始至终redis中的testlock的key只产生了9f178836-f7e1-44fe-a89d-2db52f399c0d:20这一个key,很明显这个key是属于线程thread-1的,因为thread-1先获得了锁.如果thread-2获得了线程, 在key9f178836-f7e1-44fe-a89d-2db52f399c0d:20消失后应该产生一个属于线程thread-2的key.
[外链图片转存失败(img-iEgxTxbl-1569304434563)(./_image/2019-08-08/2019-08-08-15-57-36.jpg)]

总结: 总上面两种角度的分析来看, thread-2在被别的线程中断后并没有获得锁, 所以这种写法不严谨!
demo2:

import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.config.Config;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class TestDistributedRedisLockWithBool {
    private static CountDownLatch finish = new CountDownLatch(2);
    private static final String KEY = "testlock";
    private static Config config;
    private static Redisson redisson;
    static {
        config = new Config();
        config.useSingleServer().setAddress("127.0.0.1:6379");
        redisson = (Redisson)Redisson.create(config);
    }

    public static void main(String[] args) {
        Thread thread_1 = new LockWithBoolean("thread-1");
        Thread thread_2 = new LockWithBoolean("thread-2");
        thread_1.start();
        try {
            TimeUnit.SECONDS.sleep(10); // 睡10秒钟 为了让thread_1充分运行
            thread_2.start();
            TimeUnit.SECONDS.sleep(10); // 让thread_2 等待锁
            thread_2.interrupt(); // 中断正在等待锁的thread_2 观察thread_2是否会不会拿到锁
            finish.await();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            redisson.shutdown();
        }
    }

    static class LockWithBoolean extends Thread {
        private String name;

        public LockWithBoolean(String name) {
            super(name);
        }

        public void run() {
            RLock lock = redisson.getLock(KEY);
            lock.lock(10, TimeUnit.MINUTES);
            if (!Thread.currentThread().isInterrupted()) {
                System.out.println(Thread.currentThread().getName() + " gets lock.");
                try {
                    TimeUnit.MINUTES.sleep(1);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    lock.unlock();
                }
            } else {
                System.out.println(Thread.currentThread().getName() + " does not get lock.");
            }
            System.out.println(Thread.currentThread().getName() + " ends.");
        }
    }
}

结果如下: 符合预期, 没有报异常, 线程都是正常退出.

thread-1 gets lock.
thread-2 does not get lock.
thread-2 ends.
thread-1 ends.

你可能感兴趣的:(redis锁)