• 可重入锁基本原理


     

    转自:http://www.jianshu.com/p/007bd7029faf

    简单锁

    在讲述简单锁的实现之前,我们先来看一个锁的应用例子:

    public class Counter{
        private Lock lock = new Lock();
        private int count = 0;
        public int inc(){
            lock.lock();
            this.count++;
            lock.unlock();
            return count;
        }
    }

    上面的程序中,由于this.count++这一操作分多步执行,在多线程环境中可能出现结果不符合预期的情况,这段代码称之为 临界区 ,所以需要使用lock来保证其原子性。

    Lock的实现:

    public class Lock{
        private boolean isLocked = false;
        public synchronized void lock() throws InterruptedException{
            while(isLocked){    //不用if,而用while,是为了防止假唤醒
                wait();
            }
            isLocked = true;
        }
        public synchronized void unlock(){
            isLocked = false;
            notify();
        }
    }

    说明:当isLocked为true时,调用lock()的线程在wait()阻塞。 为防止该线程虚假唤醒,程序会重新去检查isLocked条件。 如果isLocked为false,当前线程会退出while(isLocked)循环,并将isLocked设回true,让其它正在调用lock()方法的线程能够在Lock实例上加锁。当线程完成了临界区中的代码,就会调用unlock()。执行unlock()会重新将isLocked设置为false,并且唤醒 其中一个 处于等待状态的线程。

    锁的可重入性

    同样,先举例来说明锁的可重入性:

    public class UnReentrant{
        Lock lock = new Lock();
        public void outer(){
            lock.lock();
            inner();
            lock.unlock();
        }
        public void inner(){
            lock.lock();
            //do something
            lock.unlock();
        }
    }

    outer中调用了inner,outer先锁住了lock,这样inner就不能再获取lock。其实调用outer的线程已经获取了lock锁,但是不能在inner中重复利用已经获取的锁资源,这种锁即称之为 不可重入 。通常也称为 自旋锁 。相对来说,可重入就意味着:线程可以进入任何一个它已经拥有的锁所同步着的代码块。

    可重入锁的基本原理

    我们在上面的Lock基础上作一些修改:

    public class Lock{
        boolean isLocked = false;
        Thread  lockedBy = null;
        int lockedCount = 0;
        public synchronized void lock()
            throws InterruptedException{
            Thread callingThread = Thread.currentThread();
            while(isLocked && lockedBy != callingThread){
                wait();
            }
            isLocked = true;
            lockedCount++;
            lockedBy = callingThread;
      }
        public synchronized void unlock(){
            if(Thread.curentThread() == this.lockedBy){
                lockedCount--;
                if(lockedCount == 0){
                    isLocked = false;
                    notify();
                }
            }
        }
    }
    • lockBy:保存已经获得锁实例的线程,在lock()判断调用lock的线程是否已经获得当前锁实例,如果已经获得锁,则直接跳过while,无需等待。
    • lockCount:记录同一个线程重复对一个锁对象加锁的次数。否则,一次unlock就会解除所有锁,即使这个锁实例已经加锁多次了。

    重入锁实现可重入性原理或机制是:

    每一个锁关联一个线程持有者和计数器,当计数器为 0 时表示该锁没有被任何线程持有,那么任何线程都可能获得该锁而调用相应的方法;当某一线程请求成功后,JVM会记下锁的持有线程,并且将计数器置为 1;此时其它线程请求该锁,则必须等待;而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增;当线程退出同步代码块时,计数器会递减,如果计数器为 0,则释放该锁。

    什么是可重入性,为什么说 Synchronized 是可重入锁?

    可重入性是锁的一个基本要求,是为了解决自己锁死自己的情况。
    一个类中的同步方法调用另一个同步方法,假如 Synchronized 不支持重入,进入 method2 方法时当前线程获得锁,method2 方法里面执行 method1 时当前线程又要去尝试获取锁,对 Synchronized 来说,可重入性是显而易见的,刚才提到,在执行 monitorenter 指令时,如果这个对象没有锁定,或者当前线程已经拥这时如果不支持重入,它就要等释放,把自己阻塞,导致自己锁死自己。    有了这个对象的锁(而不是已拥有了锁则不能继续获取),就把锁的计数器 +1,其实本质上就通过这种方式实现了可重入性。

  • 相关阅读:
    eslint 入门项目搭建过程
    ES6 模块化笔记
    闭包
    JavaScript 内存相关知识
    Mac 配置Charles,抓取移动设备数据
    jquery.cookie的path坑
    如何模拟click事件,打开一个a标签链接?
    6月份开发问题整理
    js 淡入淡出的tab选项卡
    点击弹出模态框-以登录表单为例
  • 原文地址:https://www.cnblogs.com/shoshana-kong/p/14063001.html
Copyright © 2020-2023  润新知