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


    首先引入概念:

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

          总结如下:

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

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

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

  • 相关阅读:
    递归算法的时间复杂度
    5分钟了解lucene全文索引
    Codeforces Round #234A
    快速排序法
    构造方法、类的初始化块以及类字段的初始化顺序
    Java之方法重载篇(我重载了,你要如何来调用我。。)
    突如其来的"中断异常",我(Java)该如何处理?
    一个简单的wed服务器SHTTPD(4)————SHTTPD支持CGI的实现
    一个简单的wed服务器SHTTPD(3)————SHTTPD多客户端支持的实现
    一个简单的wed服务器SHTTPD(2)———— 客户端请求分析
  • 原文地址:https://www.cnblogs.com/edison20161121/p/10293156.html
Copyright © 2020-2023  润新知