可重入锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等等 )。这些已经写好提供的锁为我们开发提供了便利,但是锁的具体性质却很少被提及 。本章将分析JAVA下可重入锁特性,为大家答疑解惑 。
释义
广义上的可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),这样的锁就叫做可重入锁,重入锁以线程为单位,当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他的线程是不可以的 。ReentrantLock和synchronized都是可重入锁
可重入锁的意义便在于防止死锁!!!
实现原理是通过为每个锁关联一个请求计数器和一个占有它的线程 。当计数为0时,认为锁是未被占有的;线程请求一个未被占有的锁时,JVM将记录锁的占有者,并且将请求计数器置为1。
如果同一个线程再次请求这个锁,计数将递增;
每次占用线程退出同步块,计数器值将递减 。直到计数器为0,锁被释放 。
下面是一个用synchronized实现的例子:
public class ReentrantTest implements Runnable { public synchronized void get() { System.out.println(Thread.currentThread().getName()); set(); } public synchronized void set() { System.out.println(Thread.currentThread().getName()); } public void run() { get(); } public static void main(String[] args) { ReentrantTest rt = new ReentrantTest(); for(;;){ new Thread(rt).start(); } }}整个过程没有发生死锁的情况,截取一部分输出结果如下:
Thread-8492Thread-8492Thread-8494Thread-8494Thread-8495Thread-8495Thread-8493Thread-8493set()和get()同时输出了线程名称,表明即使递归使用synchronized也没有发生死锁,证明其是可重入的 。
不可重入锁
不可重入锁,与可重入锁相反,不可递归调用,递归调用就发生死锁 。
比如下面设计的不可重入锁:
public class Lock{ private boolean isLocked = false; public synchronized void lock() throws InterruptedException{ while(isLocked){wait(); } isLocked = true; } public synchronized void unlock(){ isLocked = false; notify(); }}使用该锁
public class Count{ Lock lock = new Lock(); public void print(){ lock.lock(); doAdd(); lock.unlock(); } public void doAdd(){ lock.lock(); //do something lock.unlock(); }}当前线程执行print()方法首先获取lock,接下来执行doAdd()方法就无法执行doAdd()中的逻辑,必须先释放锁 。这个例子很好的说明了不可重入锁 。
可重入锁:
接下来,我们设计一种可重入锁
public class Lock{ boolean isLocked = false; Thread lockedBy = null; int lockedCount = 0; public synchronized void lock() throws InterruptedException{ Thread thread = Thread.currentThread(); while(isLocked && lockedBy != thread){ wait(); } isLocked = true; lockedCount++; lockedBy = thread; } public synchronized void unlock(){ if(Thread.currentThread() == this.lockedBy){ lockedCount--; if(lockedCount == 0){ isLocked = false; notify(); } } }}所谓可重入,意味着线程可以进入它已经拥有的锁的同步代码块儿 。
我们设计两个线程调用print()方法,第一个线程调用print()方法获取锁,进入lock()方法,由于初始lockedBy是null,所以不会进入while而挂起当前线程,而是是增量lockedCount并记录lockBy为第一个线程 。接着第一个线程进入doAdd()方法,由于同一进程,所以不会进入while而挂起,接着增量lockedCount,当第二个线程尝试lock,由于isLocked=true,所以他不会获取该锁,直到第一个线程调用两次unlock()将lockCount递减为0,才将标记为isLocked设置为false 。
可重入锁的概念和设计思想大体如此,Java中的可重入锁ReentrantLock设计思路也是这样 。
【java:究竟什么是可重入锁?】
推荐阅读
- 你真的了解webshell是什么嘛?
- 2021年现在坐火车回家需要什么手续 一月份回家需要提前订火车票吗2021
- 梦见狗撵自己 梦到狗撵我是什么意思
- 精读《从零开始做架构》
- 虎鲸和人类的关系 虎鲸为什么对人类友好科学解释
- 梦见以前的情人不理我是什么意思 梦见情人不理你
- 淘宝店从哪里进货比较好 在淘宝上卖什么好卖
- 为什么现在很多软件都要求读取相册、位置等权限,安全吗?
- 泡脚多长时间合适 泡脚有什么好处
- 艾叶泡脚一周泡几次 艾叶泡脚有什么好处
