ReentrantLock与synchronized两种锁都具备可重入的特征,其实是个很简单的概念,可是不少人都是不看源码硬解释,致使问题变得玄乎。今天我就简单的进行一下解释,可重入实际上也就是当前获取到锁执行权限的线程,能够屡次调用加锁的过程,而不会影响线程的正常运行。测试
1、举个例子spa
家里只有一辆车,老大、老二须要使用的时候都要去老爸那儿拿钥匙。线程
周一 ,老大:“老爸,给我钥匙我要出去浪”,老爸给了他,后来老二又来要钥匙,老爸说你哥拿走了,明天轮到你。同时记录下“老大得到了汽车”(+1),晚上老大回家报道,老爸消掉记录(-1),汽车空置;3d
周二 ,老二:“老爸,给我钥匙我要出去浪”,老爸给了他,后来老大又来要钥匙,老爸说你弟拿走了,明天再来。同时记录下“老二得到了汽车”(+1),晚上老二回家报道,老爸消掉记录(-1),汽车空置;code
他俩只能一人出门一人在家吗?不是的。周三的时候老大老二手拉手一块儿出去看电影,老大找到老爸,老爸说“能够”,记录下“老大得到了汽车”(+1)。老二跟着找老爸,老爸也说“能够”,同时也作了个记录“老二也在车里”(+1)。晚上老大回家报道,老爸消掉一条记录(-1)。老二跟着报了道,老爸再消一条记录(-1)。汽车空置。可是若是老二先跑去上厕所忘了跟老爸报备,结果汽车仍然被标记占用,那么你们就都用不了了。对象
这个例子中,汽车就是咱们的锁,老大、老二单独行动就是两个独立的线程,一块儿行动的话就是一个线程。老爸的记录就是ReentrantLock对象里的 State变量,这个变量是volatile线程共享的。当state=0时,即为资源空置,锁被线程获取后 state将从0 -> 1。当线程中嵌套的内部方法又去请求锁的时候,ReentrantLock会去判断当前线程是否已经获取了锁,若是是,则容许内部方法继续运行,不过此时 state状态要加 1,即从 1 -> 2。ReentrantLock的这个机制就是可重入,若是子方法中还有子方法那么这个值将会从2 -> 3甚至无限大。一个线程重复获取锁的时候,须要成对的去释放锁,将state从n一直减为0,释放资源。少一次释放,资源就被锁死;反过来,那么不可重入就是说当线程已经得到了锁,那么子方法是不容许再写获取锁的逻辑了,不然当前线程也被挂起。blog
2、写个测试代码资源
/** * 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(); } }); } } }
3、看看源码get
写在最后源码
可重入锁实际上就这么简单,state每重入一次就加1,每释放一次就减1。原理很简单,我我的以为这么作只是为了减小锁机制的使用难度,使用者只用关注方法是否完成了获取锁与释放锁的逻辑。而不用去管调用链上是否已经有人占据了锁。