ReentrantLock入门之路

AQS概述

一、 AQS本质就是AbstractQueuedSynchronizer类;

1、AQS一些说明

在AQS中有一些属性和一个双向对列(CLH对列);
AQS是并发包下的一个基类,AQL中关键的内部类;

2、node对象

 static final class Node {
 	//独占锁/排它锁 标识
  static final Node EXCLUSIVE = null;
  			//如果有这个标识, 证明已经失效了
          	static final int CANCELLED =  1;
        	//如果有这个标识, 说明后续节点需要被唤醒
        	 static final int SIGNAL    = -1;
           //node 对象储存标识的地方
		   volatile int waitStatus;
		   //上一个节点
		  volatile Node prev;
		  //下一个节点
	     volatile Node next;
	     //当前node 绑定的线程
      volatile Thread thread;
      //返回前驱节点/上一个节点,
   final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

 }

3、AbstractQueuedSynchronizer中包含

记录头节点
private transient volatile Node head;
记录尾部节点
private transient volatile Node tail;
Node

ReentrantLock可以实现公平锁也可以实现非公平锁,同时也是互斥锁以及可重入锁

二、ReentrantLock加锁

1、进入lock

  public void lock() {
    //sync 分为公平与非公平锁
    //有俩种实现
        sync.lock();
    }

2、从非公平锁实现进入

    final void lock() {
    //通过CAS(比较交换)的方式尝试将state由0改为1,返回true表示成功,返回false,表示失败
            if (compareAndSetState(0, 1))
            	//将一个属性设置为当前线程,这个属性是由AQS父类提供
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

3、 acquire方法

  public final void acquire(int arg) {
  		//再次尝试获取锁资源,tryAcquire(arg) 返回true 表示已经拿完锁资源,
        if (!tryAcquire(arg) &&
        	//!tryAcquire(arg) 获取资源失败, 需要将当前线程封装成node, 追加到AQS对列中
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            //
            selfInterrupt();
    }

4、acquireQueued方法

      final boolean nonfairTryAcquire(int acquires) {
      		//获取当前线程
            final Thread current = Thread.currentThread();
            //获取AQL中state值
            int c = getState();
            //如果AQS中状态为0 表示上一个线程已经执行完毕,可以竞争锁
            if (c == 0) {
            	//通过CAS尝试将AQS中状态由0改为1,返回成功,设置setExclusiveOwnerThread属性为当前线程
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //如果状态不为0,肯定有线程获取到了锁,判断是否为当前线程
            else if (current == getExclusiveOwnerThread()) {
            	//如果是当前线程则,为AQS状态加 acquires
                int nextc = c + acquires;
                //如果重入加锁太多超过最大值,抛出加锁最大异常
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                //成功累加,重新赋值state
                setState(nextc);
                //锁重入成功
                return true;
            }
            return false;
        }

5、addWaiter(Node.EXCLUSIVE)方法

	//前边获取锁资源失败,放到对列中等待
   private Node addWaiter(Node mode) {
   		//创建node 设置当前线程为新node对象, 同时设置为 排它锁 mode =Node.EXCLUSIVE
        Node node = new Node(Thread.currentThread(), mode);
        //获取AQS最后一个节点node
        Node pred = tail;
    
        if (pred != null) {
        	//最后一个不为空,说明对列中有等待节点,将当前节点的前一个节点设置为,最开始的最后一个节点
            node.prev = pred;
            //通过CAS的方式尝试 将AQS的tail最后一个节点设置为当前节点
            if (compareAndSetTail(pred, node)) {
            	//设置成功,将当前节点设置为最后一个节点
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }

	//enq方法 尾部节点为空时执行,
    private Node enq(final Node node) {
        for (;;) {
        	//继续尝试获取尾节点
            Node t = tail;
            //如果继续为空
            if (t == null) { // Must initialize
            	//使用CAS 设置一个无意义的头节点
                if (compareAndSetHead(new Node()))
                	//同时尾节点也指向当前节点
                    tail = head;
            } else {
            //存在尾结点,将当前节点的上一个节点设置为t,
                node.prev = t;
                  //通过CAS的方式尝试 将AQS的tail最后一个节点设置为当前节点
                if (compareAndSetTail(t, node)) {
                //设置成功,将当前节点设置为最后一个节点
                    t.next = node;
                    return t;
                }
            }
        }
    }

6、acquireQueued方法

//	已经将node加入到双向对列中后,执行此方法
    final boolean acquireQueued(final Node node, int arg) {
    		//设置一个标识
        boolean failed = true;
        try {
        	//设置一个标识
            boolean interrupted = false;
            for (;;) {
            	//获取当前节点的前驱节点/前一个节点
                final Node p = node.predecessor();
                //如果P节点是头节点,当前节点尝试再次获取锁资源(state 0->1,或者可重入),成功true
                //失败false
                if (p == head && tryAcquire(arg)) {
                	//获取锁资源成功,将当前节点设置为头节点
                    setHead(node);
                    //将之前的头节点置空,利于GC
                    p.next = null; 
                    //修改标识,
                    failed = false; 
                    //返回标识
                    return interrupted;
                }
                //没拿到锁资源,
                if (shouldParkAfterFailedAcquire(p, node) &&
                	//shouldParkAfterFailedAcquire方法返回true时,阻塞当前线程等待唤醒
                	//基于UNSAFE.park方法挂起线程
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
//pred上一个节点。node当前节点  当前方法作用保证上一个节点为-1时 才返回true
 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
 		//获取上一个节点的状态
        int ws = pred.waitStatus;
        //上一个节点状态为SIGNAL=1 时 正常。
        if (ws == Node.SIGNAL)
            return true;
        //如果上一个节点失效了,
        if (ws > 0) {
            do {
            // 将当前节点的上一个节点,指向到上一个节点的前一个节点
            //C -->B-->A  原指向规则
            //C-->A		 修改后指向规则
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            //将重新声明好的上一个节点指向为当前节点
            // A -->B -->C  原指向规则
            //A-->C         修改后指向规则
            pred.next = node;
        } else {
          	//当ws  小于等于0,并且不等于-1 时 ,将当前节点的上一个节点设置为-1 ,只有上一个节点
          	//状态为-1 时,才可以正常唤醒下一个节点
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

三、ReentrantLock释放锁

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值