Error message here!

Hide Error message here!

忘记密码?

Error message here!

请输入正确邮箱

Hide Error message here!

密码丢失?请输入您的电子邮件地址。您将收到一个重设密码链接。

Error message here!

返回登录

Close

可重入锁和不可重入锁,递归锁和非递归锁

痞子陈2016 2019-01-19 22:47:00 阅读数:182 评论数:0 点赞数:0 收藏数:0

首先引入概念:

可重入锁:广义上的可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者class),这样的锁就叫做可重入锁,

java里面最常见的锁,ReentrantLock和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 Test{
Lock lock = new Lock();

/**
调用打印的方法
*/
public void print(){ lock.lock(); doAdd(); lock.unlock(); } public void doAdd(){ lock.lock(); //sout("执行业务代码") lock.unlock(); } }

场景说明:假设某业务下需要调用Test类里面的print()方法,假设他的线程命名为T0,这时T0会执行lock.lock()方法,首先对于这个对象来说,isLocked属性的初始值时false,因此它进入while循环的时候

判断为false,直接跳出当前循环,把对象的isLocked属性变为true,相当于拿到了锁,这时T0再去执行doAdd(),由于要保证原子性,因此在doAdd方法里面也加入了lock锁,这时,线程还是T0线程,但

由于isLocked属性由于第一次加锁已经变成true,因此,T0线程执行到了wait()方法就处于等待,导致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();
}
}
}
}
public class Test{
Lock lock = new Lock();
/**
调用打印的方法
*/
public void print(){
lock.lock();
doAdd();
lock.unlock();
}
public void doAdd(){
lock.lock();
//sout("执行业务代码")
 lock.unlock();
}
}

       场景如上描述,假设线程T0进来了,调用print方法,lock.lock(),第一步首先拿到当前线程,由于初始的islocked为false,同时lockedby为null 和当前线程T0不相等,false &&true 得到还是false ,因此直接跳出while循环,线程不等待,将isLocked设置为true,同时设置当前锁的数量从0加上1变成1,并且设置lockby为当前线程T0,此时T0继续执行doAdd方法,当执行doAdd()里面的lock.lock()时,同样还是线程T0,因此while循环的判断变成了true&& false,最终拿到的还是false,这时线程还是不等待,isLocked还是true,同时当前线程拥有的锁变成了2,lockedby还是T0,这时假设又有T1,T2线程进来,当他们执行print()方法,执行到了lock.lock(),首先拿到当前线程是T1,而lockedby是T0,while循环的条件判断是true&&true,则T1就处于了等待状态,只有当T0执行完doAll()的业务代码,并第一次释放锁,lock.unlock(),当前线程的计数器减去1,这时T0再去执行print方法里面的lock.unlock(),这时线程T0,计数器变量变成了0,同时设置isLocked为false,执行notify方法,唤醒其他的线程,后续线程抢夺资源拿到锁之后,即可实现同步安全的执行。

      总结如下:

      可重入锁,也叫做递归锁,指的是同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响。

      不可重入锁,也可以叫非递归锁,就是拿不到锁的情况会不停自旋循环检测来等待,不进入内核态沉睡,而是在用户态自旋尝试。

       同一个线程可以多次获取同一个递归锁,不会产生死锁。而如果一个线程多次获取同一个非递归锁,则会产生死锁。

版权声明
本文为[痞子陈2016]所创,转载请带上原文链接,感谢
https://www.cnblogs.com/edison20161121/p/10293156.html

编程之旅,人生之路,不止于编程,还有诗和远方。
阅代码原理,看框架知识,学企业实践;
赏诗词,读日记,踏人生之路,观世界之行;

支付宝红包,每日可领