AQS-lock【源码分析】

AQS分析

lock.lock()过程

1.锁的基本要素:一个共享的的数据来记录锁的状态,AQS采用State作为锁标记,0为无锁,大于等于1是有锁状态
2.NonfairSync类代码片段

        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

(1)获得锁成功:
compareAndSetState是CAS->乐观锁,原子性

protected final boolean compareAndSetState(int expect, int update) {
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

即将expect和stateOffset相比如果相等则更新为update否则不更新,整个过程是原子的
compareAndSetState(0, 1)为true则获得锁成功执行,即将当前线程获得独占锁:
setExclusiveOwnerThread(Thread.currentThread());

protected final void setExclusiveOwnerThread(Thread thread) {
      	  exclusiveOwnerThread = thread;
}

(2)获得锁失败:
如果获得锁失败则走else,acquire(1);

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

①首先分析tryAcquire(arg):

protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
 final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

首先获取一次锁的状态,如果为无锁状态则采用CAS将当前线程设置为有锁状态,返回true
这样好处在于,线程执行效率如果快的话,再比较一次可能更加的有效率,每一个线程都可以在这再尝试一次是否可获得锁,这也是一种插队机制。

若不为无锁状态,则进行判断是否为重入锁,如果是则走else if一下逻辑,此段主要是对重入锁进行重入次数的计数并设置State的值

若以上两段都不满足则返回false,并且由于!tryAcquire(arg)所以走到了这一段代码acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

②分析acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
首先分析addWaiter(Node.EXCLUSIVE) 参数说明:Node.EXCLUSIVEnull arg1

private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
//尝试快速询问,故障时备份到完整的询问
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }

先来分析enq(node)

 private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
}

For(;;)代表死循环,以下图为例说明:
在这里插入图片描述

在第一次循环时,tail==null,所以走第一个if,然后紧接着CAS比较Head是否和null相等,如果相等则将Head设置为一个空的Node即Node(){} ;所以如上图step2所示,head指向了一个空节点Node,最后将tail = head。

在第二次循环时,tail已不为空(指向空节点),走else语句:代码如下所示

node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;

node为形参传入的node,它包装了那个线程,例如ThreadB,以ThreadB为例演示以下过程,首先node.prev指向t; CAS比较t跟内存中的tail是否相等,若相等则将tail更新为node即指向node,t.next=node,返回t;请对照上图step3看

若ThreadC进来,则不会进入enq(node)方法了,直接在以下代码处返回

 private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail; //此处为ThreadC进来
        if (pred != null) { //此处不为空
            node.prev = pred; //将node的prev指向上一个尾节点
            if(compareAndSetTail(pred, node)){ //将尾节点换成当前节点Node(ThreadC)
                pred.next = node;  //pred的next指向Node(ThreadC)
                return node;		//返回Node(ThreadC)
            }
        }
        enq(node);
        return node;
    }

此时会返回到上面的②acquireQueued(addWaiter(Node.EXCLUSIVE), arg)),则第一个Thread传递的为acquireQueued(Node(ThreadB),1);现在开始分析acquireQueued:

 final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {		//首先是死循环
                final Node p = node.predecessor(); //在下面进行分析1),返回prev节点
                if (p == head && tryAcquire(arg)) { //若第一次p==head为true 2)
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) && //下面分析这个if 3)
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
}

1)node.predecessor()代码:

final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

判断prev是否为null,若是报错,否则返回p

2)tryAcquire代码:再次去尝试获取锁的那段代码

 final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

3)shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()
1.shouldParkAfterFailedAcquire(p, node)

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
 int ws = pred.waitStatus; //ws默认为0
 if (ws == Node.SIGNAL)   // SIGNAL为-1
      return true;
 if (ws > 0) {			 // 大于0的只有CANCELLED==1
      do {
          node.prev = pred = pred.prev;
      } while (pred.waitStatus > 0);
      pred.next = node;
 } else {
      compareAndSetWaitStatus(pred, ws, Node.SIGNAL); //将状态设置为SIGNAL
 }
   return false;
}

2.parkAndCheckInterrupt()若想进入此方法,一定是第二个线程过来时

private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this); //将当前线程挂起
        return Thread.interrupted();	//返回是否被interrupt过,若是则返回true,并复位,默认为false
}

此时线程被挂起,如下图step4所示
在这里插入图片描述

由下面的lock.unlock()中的unpark()时
回到这段代码

 final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) { //此时满足,因为在lock.unlock()执行unpark之后
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
}

tryAcquire(arg) ThreadB抢占到锁,并改变state和exclusiveOwnerThread为自己,如下图代码所示:

if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
             }

分析这一段代码

setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;

第一步:SetHead做的事情将Head指向ThreadB,将ThreadB中的thread属性设置为null,将指向之前的head节点设置为指向null

private void setHead(Node node) {
        head = node;
        node.thread = null;
        node.prev = null;
    }

第二步:将p的next属性设置为null,最终如下图所示:

在这里插入图片描述

lock.unlock()过程
猜测一下,首先释放锁肯定会将锁状态设置为0,以及线程设置为空,其次肯定会唤醒下一个等待的节点,使得去抢占锁
1 sync.release(1)

public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
      return false;
    }

1.1 tryRelease(arg)

 protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

将exclusiveOwnerThread设置为null,将state设置为0,即猜测第一步

1.2 继续往下走

  Node h = head;
            if (h != null && h.waitStatus != 0) //满足条件
                unparkSuccessor(h);
            return true;

1.3 unparkSuccessor(h)

private void unparkSuccessor(Node node) {
        int ws = node.waitStatus; 	// ws==-1
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0); //将head节点的waitstatus设置为0
        Node s = node.next;					//s即为ThreadB
        if (s == null || s.waitStatus > 0) {			//s不为null
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread); 		//走到这,将ThreadB唤醒
    }

前面代码遗留两个问题
1

Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }

为什么要在尾部向后遍历找到实际的,因为可能会出现下图情况
在这里插入图片描述

若从前往后找有可能会出现next没引用上的问题

2
if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
在此处被interrupt中断后,在这里相应:

static void selfInterrupt() {
        Thread.currentThread().interrupt();
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章