成功的路上总是离不开贵人的帮助,名师的指点和小人的刺激。

莫怕,过了桥,就翻篇了

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

首先引入概念:

可重入锁:广义上的可重入锁指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁(前提得是同一个对象或者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方法,唤醒其他的线程,后续线程抢夺资源拿到锁之后,即可实现同步安全的执行。

      总结如下:

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

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

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

posted on 2019-01-19 22:47  痞子陈2016  阅读(1644)  评论(1编辑  收藏  举报

导航