简话ReentrantLock的可重入锁概念

    ReentrantLock与synchronized两种锁都具有可重入的特征,实际上是个很简单的概念,但是很多人都是不看源码硬解释,导致问题变得玄乎。今天我就简单的进行一下解释,可重入实际上也就是当前获取到锁执行权限的线程,可以多次调用加锁的过程,而不会影响线程的正常运行。

一、举个例子

    家里只有一辆车,老大、老二需要使用的时候都要去老爸那儿拿钥匙。

 周一 ,老大:“老爸,给我钥匙我要出去浪”,老爸给了他,后来老二又来要钥匙,老爸说你哥拿走了,明天轮到你。同时记录下“老大获得了汽车”(+1),晚上老大回家报道,老爸消掉记录(-1),汽车空置;

 周二 ,老二:“老爸,给我钥匙我要出去浪”,老爸给了他,后来老大又来要钥匙,老爸说你弟拿走了,明天再来。同时记录下“老二获得了汽车”(+1),晚上老二回家报道,老爸消掉记录(-1),汽车空置;

 他俩只能一人出门一人在家吗?不是的。周三的时候老大老二手拉手一起出去看电影,老大找到老爸,老爸说“可以”,记录下“老大获得了汽车”(+1)。老二跟着找老爸,老爸也说“可以”,同时也做了个记录“老二也在车里”(+1)。晚上老大回家报道,老爸消掉一条记录(-1)。老二跟着报了道,老爸再消一条记录(-1)。汽车空置。但是如果老二先跑去上厕所忘了跟老爸报备,结果汽车仍然被标记占用,那么大家就都用不了了。

  这个例子中,汽车就是我们的锁,老大、老二单独行动就是两个独立的线程,一起行动的话就是一个线程。老爸的记录就是ReentrantLock对象里的 State变量,这个变量是volatile线程共享的。当state=0时,即为资源空置,锁被线程获取后 state将从0 -> 1。当线程中嵌套的内部方法又去请求锁的时候,ReentrantLock会去判断当前线程是否已经获取了锁,如果是,则允许内部方法继续运行,不过此时 state状态要加 1,即从 1 -> 2。ReentrantLock的这个机制就是可重入,如果子方法中还有子方法那么这个值将会从2 -> 3甚至无限大。一个线程重复获取锁的时候,需要成对的去释放锁,将state从n一直减为0,释放资源。少一次释放,资源就被锁死;反过来,那么不可重入就是说当线程已经获得了锁,那么子方法是不允许再写获取锁的逻辑了,否则当前线程也被挂起。

二、写个测试代码

/**
 * ReentrantLock 测试
 */
public class ReentrantLockTest2 {
    public ReentrantLock lock = new ReentrantLock();
    /**
     * 外层方法加锁
     * @throws InterruptedException
     */
    public void test() throws InterruptedException {
        lock.lock();
        try {
            test2();
            // System.out.print("当前锁获得线程:" + Thread.currentThread().getName() + "
");
        } catch (Exception e) {

        } finally {
            lock.unlock();
        }
    }
    /**
     * 内层方法加锁
     * @throws InterruptedException
     */
    public void test2() throws InterruptedException {
        lock.lock();
        try {
            test();
            System.out.print("当前锁获得线程:" + Thread.currentThread().getName() + "
");
        } catch (Exception e) {

        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws Exception {
        ReentrantLockTest2 testObj = new ReentrantLockTest2();
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 2; i++) {
            int finalI = i;
            executorService.submit(() -> {
                try {
                    Thread.currentThread().setName("任务"+ finalI);
                    testObj.test();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
    }
}

三、看看源码

 

 写在最后

  可重入锁实际上就这么简单,state每重入一次就加1,每释放一次就减1。原理很简单,我个人觉得这么做只是为了减少锁机制的使用难度,使用者只用关注方法是否完成了获取锁与释放锁的逻辑。而不用去管调用链上是否已经有人占据了锁。

原文地址:https://www.cnblogs.com/MrSi/p/13982923.html