简话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() + "\r\n"); } catch (Exception e) { } finally { lock.unlock(); } } /** * 内层方法加锁 * @throws InterruptedException */ public void test2() throws InterruptedException { lock.lock(); try { test(); System.out.print("当前锁获得线程:" + Thread.currentThread().getName() + "\r\n"); } 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。原理很简单,我个人觉得这么做只是为了减少锁机制的使用难度,使用者只用关注方法是否完成了获取锁与释放锁的逻辑。而不用去管调用链上是否已经有人占据了锁。