<返回更多

java:究竟什么是可重入锁?

2019-10-31    
加入收藏

可重入锁作为并发共享数据,保证一致性的工具,在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-8492
Thread-8492
Thread-8494
Thread-8494
Thread-8495
Thread-8495
Thread-8493
Thread-8493

set()和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设计思路也是这样。

声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多资讯 >>>