不可重入锁:
public class test {
Lock lock=new Lock();
public void methon1(){
lock.lock();
System.out.println("锁被占用");
methon2();
lock.unlock();
}
public void methon2(){
lock.lock();
System.out.println("锁被占用");
lock.unlock();
}
当methon1方法获取lock锁去锁住一段需要做原子性操作的methon2方法时,如果这段methon2方法又需要锁去做原子性操作,那么methon1方法就必定要与methon2方法出现死锁。这种会出现问题的重入一把锁的情况,叫不可重入锁。
这时候我们可以去看看不可重入锁中lock方法的源码:
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();
}
}
但是在现实需求中我们又有需要重入一把锁的需求,所以我们不得不引入可重入锁。
下面我们就来看看可重入锁的lock方法和unlock方法的源码:
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();
}
}
}
}
lockedBy:这些资源被哪个线程锁住了。
执行lock方法时,先获取该线程的信息:
Thread thread = Thread.currentThread();
然后在while里面判断它是否被锁了和这些资源被锁的线程和该线程是不是一致的,如果条件成立,那么该线程就必须等待,如果条件不成立,那么该线程有加了一把锁(lockedCount++)。
在unlock方法中,我们加了几把锁就必须解几次锁,所以就需要一个变量来参照——lockedCount。
综上所述:
可重用锁和不可重入锁的设计不同之处:
不可重入锁:只判断这个锁有没有被锁上,只要被锁上申请锁的线程都会被要求等待。实现简单
可重入锁:不仅判断锁有没有被锁上,还会判断锁是谁锁上的,当就是自己锁上的时候,那么他依旧可以再次访问临界资源,并把加锁次数加一。
设计了加锁次数,以在解锁的时候,可以确保所有加锁的过程都解锁了,其他线程才能访问。不然没有加锁的参考值,也就不知道什么时候解锁?解锁多少次?才能保证本线程已经访问完临界资源了可以唤醒其他线程访问了。实现相对复杂。