AQS reentrantlock加锁流程

AQS 与ReentrantLock ,ReentrantReadWriteLock 解析

ReentrantLock 与 AQS 框架源码解析

预备知识aqs 大致内部结构如下图所示
内部结构

CAS 机制

内存值V、旧的预期值A、要修改的值B,当且仅当预期值A和内存值V相同时(条件),将内存值修改为B并返回true,否则条件不符合返回false。条件不符合说明该变量已经被其它线程更新了。
名词解释:
node head/tail: 维护的是线程的一个FIFO队列;
state: volatile修饰的一个常量(内存可见性,防止指令重排序);
exclusiveOwnerThread: 当前线程持有者如t1线程;
重要方法:
tryAcquire :尝试加锁抽象方法需要自己实现重点看下面 reentrantlock解析;
acquire:当尝试加锁失败,会进入FIFO队列排队;
tryRelease: 抽象方法子类实现;
release: 解锁操作;

ReentrantLock 解读

ReentrantLock 分为公平与非公平锁 默认非公平锁
创建公平锁
ReentrantLock lock =new ReentrantLock(true);//公平锁
ReentrantLock lock =new ReentrantLock();//非公平锁
加锁过程:

    /**
      * 加锁
      */
      final void lock() {
           acquire(1);
       }
     /**
      * 调用此方法加锁
      */
      public final void acquire(int arg) {
        // 尝试加锁 加锁失败进入队列排队
       if (!tryAcquire(arg)&&acquireQueued(addWaiter(Node.EXCLUSIVE), arg)){
           selfInterrupt();
           }
   }
   /**
   * 公平锁 分析tryAcquire 方法
   * 非公平与公平锁的区别是缺少hasQueuedPredecessors()此方法
   * 非公平锁直接设置值抢锁
   */
   protected final boolean tryAcquire(int acquires) {
       final Thread current = Thread.currentThread();//获取当前线程
        int c = getState();//获取AQS state 值
        if (c == 0) {//当线程第一次进来 c==0 
            /**
             * 1、首先 判断当前线程前面还有没有线程在排队有排队返回true 否则false
             * 2、第一条件false ,没有则设置成功拿到锁 并把线程持有者为当前线程 
             */
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        //判断当前线程是否等于自己 为自己 表示锁的可重入 state + 1
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
    /**
    * 1、第一种可能如果线程第一次进来 前面没有元素 那么返回false
    * 2、如果 h != t 为true 则表示队列里面>=2个元素
    * 3、看自己是否为第二个节点,若是第二个或者是下一个(不是当前线程)则排队不拿锁
    */
   public final boolean hasQueuedPredecessors() {
        Node t = tail; //尾部节点
        Node h = head; //头部节点
        Node s;
      return h != t &&
        ((s = h.next) == null || s.thread !=  Thread.currentThread());
    }
    /**
     * mode  传过来的值 ==null
     * 只有拿锁失败 才会加入队列等待唤醒
     * 这里可以看成t1 抢锁成功 t2 失败
     */
  private Node addWaiter(Node mode) {
        //获取当前线程内存创建一个node
        Node node = new Node(Thread.currentThread(), mode);
        // 第一个线程进来tail 肯定为null 走enq方法
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }
    /**
     * 循环比较
     * 形成队列
     * node 代表t2 进来
     */
    private Node enq(final Node node) {
        for (;;) {
            //第一进来 tail ==null
            Node t = tail;
            if (t == null) { 
                 //当 tail ==null 的时候new node 并赋值给t
                if (compareAndSetHead(new Node()))
                    tail = head; //t.tail =head 
            } else {
                // t2.prev 指向前一个t
                node.prev = t;
                // 并把当前的tail 指向node t2
                if (compareAndSetTail(t, node)) {
                    t.next = node; // t.next 指向t2 .node
                    return t;
                }
            }
        }
    }
    /**
     * 判断线程是否需要睡眠
     */
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            //自旋
            for (;;) {
               // 这儿可以理解为t2 前一个节点t
                final Node p = node.predecessor();
              //t是head尝试加锁失败走下面shouldParkAfterFailedAcquire方法
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
              //第一次 false 第二次遍历时候true 则走&&后面方法park 当前线程
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
    /**
     * 抢锁失败 后则需要线程park
     */
    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;//第一次ws ==0
        if (ws == Node.SIGNAL) //-1
            return true;
        if (ws > 0) {
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else { 
         // 0的时候 设置前一个节点t.waitStatus ==-1 下次循环遍历返回true     
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

最终enq 执行后的示意图如下
t.next 指向t2 t2.prev指向t 构成双向队列在这里插入图片描述

补充 常见几种锁的区别

  1. 公平锁 : 需要排队依次进行
  2. 非公平锁:竞争抢锁
  3. 重量级锁:如synchronized
  4. 轻量级锁:如reentrantlock
  5. 自旋锁:如reentrantlock /reentrantreadwritelock
  6. 可重入锁:reentrantlock、synchronized锁的可重入解释是当一个方法用synchronized修饰了method1 里面调用了synchronized 修饰的method2 那么方法二是可以执行的。
  7. 读写锁:reentrantreadwritelock 读读并行 ,读写互斥 ,写写互斥,读锁不能升级为写锁,

下一篇
讲解 reentrantlock 锁的唤醒

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值