java AQS源码解析

大部分的锁,像可重入锁,读写锁,countdownLatch等都是基于AQS实现。

1:AbstractQueuedSynchronizer是一个抽象方法,有一些方法是需要子类进行实现的,这里面就用到了模板的设计模式(不会请自行百度)

2:内部类Node节点,存储当线程获取不到锁的时候,进行锁的保存形成一个队列(FIFO),当释放锁的时候,就会在Node节点开始获取线程,拿到锁。

首先介绍node节点

 

构造函数,三个方法。第一个是空的,什么也没有操作。

第二个 thread赋值给Thread,int 赋值到waitStatus。

第二个 thread赋值给Thread,mode赋值到nextWaiter。

线程等待模式

SHARED:以共享的方法等待锁,一把锁可以锁多个线程

EXCLUSIVE:以互斥的方式等待锁,一把锁只能锁一个线程。

 

线程的状态

CANCELLED:等于1,表示线程获取锁已经取消了。

SIGNAL:等于-1,表示线程在等待,等待获取锁。。

CONDITION:等于-2,表示线程等待某一条件被满足(condition)。

PROPAGATE:等于-3,线程处于共享模式的时候,才会被用到。

waitStatus:该字段表示线程的状态,被volatile修饰。因为是int,所以默认值是0。

其他成员变量

prev:该节点的上一个节点,类型是Node。

next:该节点的下一个节点,类型是Node。

thread:代表该节点的线程,类型是Thread。

nextWaiter:等待condition条件的node节点。类型是node。

 

介绍AQS类中的重要方法

private transient volatile Node head;  //表示当前的首节点。

private transient volatile Node tail;  //表示当前的尾节点。

private volatile int state;  //这个是是重点的属性,标识状态。

 

acquire方法是一个重点方法

//调用tryAcquire方法,其次会调用addWaiter方法,在调用acquireQueued方法,将节点添加到队列中。

public final void acquire(int arg) {
  if (!tryAcquire(arg) &&
  acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
  selfInterrupt();
}

 

//protected修饰的,都是需要子类去实现的,这个就是使用了模板方法

protected boolean tryAcquire(int arg) {
  throw new UnsupportedOperationException();
}

    private Node addWaiter(Node mode) {
Node node
= new Node(Thread.currentThread(), mode);  //创建处一个节点
     Node pred = tail;  //获取尾节点
        if (pred != null) {  
            node.prev = pred; //将尾节点设置成为当前节点的上一节点
            if (compareAndSetTail(pred, node)) { //通过CAS获取到是否修改成功,这里面主要是多个线程在获取锁
                pred.next = node;
                return node;
            }
        }
        enq(node); //当没有获取到锁的时候,执行enq方法
        return node;
    }


  private Node enq(final Node node) { 
  for (;;) { //设置一个死循环
  Node t = tail; //获取尾节点
   if (t == null) { //如果为空则将首节点设置为尾节点
   if (compareAndSetHead(new Node()))
   tail = head;
   } else { //否则就重复上面的操作,一直不成功就一直在操作,直到成功为止
   node.prev = t;
   if (compareAndSetTail(t, node)) {
   t.next = node;
   return t;
   }
   }
   }
  }
  //上面的操作只是封装Node节点,下面是将节点存放到队列当中
  final boolean acquireQueued(final Node node, int arg) {
   boolean failed = true;
   try {
   boolean interrupted = false;
   for (;;) { //定义一个死循环
   final Node p = node.predecessor(); //代码在Node类中,获取当前节点的上一节点,如果存在就返回,不存在就报错。
   if (p == head && tryAcquire(arg)) { //如果上一节点是首节点,并且获取到了锁。
   setHead(node);  //首节点是当前节点
  p.next = null; // help GC //将上一节点的下一节点设置为null
  failed = false; //设置为false,并且返回false对象
  return interrupted;
  }
  if (shouldParkAfterFailedAcquire(p, node) && //说明上一节点不是首节点,
  parkAndCheckInterrupt()) //阻塞当前的线程,调用了LuckSupport.park方法
  interrupted = true;
  }
  } finally {
  if (failed)
  cancelAcquire(node);
  }
  }
  private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {//pred是上一节点,node是当前节点
  int ws = pred.waitStatus; //获取上一节点的状态
  if (ws == Node.SIGNAL) //如果是-1,则返回true
  return true;
  if (ws > 0) { //如果是大于0,说明上一节点已经放弃获取锁了
   do {
   node.prev = pred = pred.prev; //然后就去找上一节点的上一节点,直到找到状态小于0的
  } while (pred.waitStatus > 0);
     pred.next = node; //将符合条件的节点,下一节点设置成当前节点。
  } else {
  compareAndSetWaitStatus(pred, ws, Node.SIGNAL); //如果ws小于0,则将上一节点通过CAS设置成-1
   }
   return false;
  }

以上就是将Node节点添加队列的尾节点。

 

以下就是释放独占式的锁

    public final boolean release(int arg) {
        if (tryRelease(arg)) { //该方法是protected修饰的,需要子类实现
            Node h = head; //获取首节点
            if (h != null && h.waitStatus != 0) //判断是否等于null,状态不等于0
                unparkSuccessor(h); 
            return true;
        }
        return false;
    }

  private void unparkSuccessor(Node node) {
   int ws = node.waitStatus; //获取当前的状态
  if (ws < 0)
   compareAndSetWaitStatus(node, ws, 0); //如果小于0,设置为0

   Node s = node.next; //获取该节点的下一个节点
  if (s == null || s.waitStatus > 0) { //当下一个节点为空,或者是大于0的时候,说明该节点不符合规则,就找符合节点的节点
  s = null;
  for (Node t = tail; t != null && t != node; t = t.prev) //现在是从后面往前面找,找到小于0的节点
  if (t.waitStatus <= 0)
  s = t; //将该节点的下一个节点设置成为找的小于0的节点
  }
  if (s != null)
  LockSupport.unpark(s.thread); //调用LockSupport.unpark方法
  }

 

共享式的获取锁,可以多个线程获取锁

    public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0) //tryAcquiredShard方法是protected修饰的,子类具体方法。
            doAcquireShared(arg); //当小于0的时候,会调用该方法
    }

  
private void doAcquireShared(int arg) {
      final Node node = addWaiter(Node.SHARED); //node.Shared是一个new node。和独占式的以为区别是Node.EXCLUSIVE
      boolean failed = true; 
   try {
   boolean interrupted = false;
   for (;;) {
   final Node p = node.predecessor(); //获取当前节点的上一个节点
  if (p == head) { //上一节点是首节点
   int r = tryAcquireShared(arg); //去执行子类的该方法,尝试是否获取到了锁,
  if (r >= 0) { //如果返回值大于0,说明获取到了锁
   setHeadAndPropagate(node, r);
  p.next = null; //将上一节点的下一节点设置为null
  if (interrupted)
   selfInterrupt();
  failed = false;
  return;
  }
  }
         //该代码和独占式的代码一样
  if (shouldParkAfterFailedAcquire(p, node) &&
  parkAndCheckInterrupt())
  interrupted = true;
  }
  } finally {
   if (failed)
   cancelAcquire(node);
  }
  }

  private void setHeadAndPropagate(Node node, int propagate) {
   Node h = head; //获取首节点
  setHead(node); //将当前节点设置为首节点
    //首节点还不是当前的节点,判断之前的首节点和现在的首节点的状态是否小于0
  if (propagate > 0 || h == null || h.waitStatus < 0 ||
   (h = head) == null || h.waitStatus < 0) {
  Node s = node.next; //获取当前状态的下一个节点
  if (s == null || s.isShared()) //如果当前节点的下一个节点是null,获取是判断是否是共享式的,则调用doRealseaShard方法
   doReleaseShared();
  }
  }

 

共享式的释放锁

    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) { //该方法是protected修饰的,具体实现是父类进行的
            doReleaseShared(); //当释放成功时,调用该方法
            return true;
        }
        return false;
    }

  private void doReleaseShared() {
   for (;;) { //定义死循环
   Node h = head; //获取首节点
  if (h != null && h != tail) {
   int ws = h.waitStatus; //获取当前锁的状态
  if (ws == Node.SIGNAL) {
   if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) //将-1变成0
   continue;
  unparkSuccessor(h); //执行的方法和独占式的一样
  }
  else if (ws == 0 &&
   !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) //将0变成-3
  continue;
  }
  if (h == head) //如果是首节点没有变则返回
   break;
   }
  }
 

 

以上就是个人对AQS的理解。

 

posted @ 2020-03-08 00:22  陌然浅笑  阅读(133)  评论(0)    收藏  举报