AbstractQueuedSynchronizer 简单分析

简介

基于FIFO队列的同步框架可以实现锁与同步器,AQS是所有同步器的基类,通过CAS+volatile自旋方式改变同步器状态,提供Condition类来阻塞与唤醒当前线程
与Object对象的wait和singal方法效果一样
子类通过非public内部静态类实现所需同步器并对外提供加锁(支持公平锁与非公平锁),解锁方法
本文不会完全分析该类所有方法重点分析几个模版方法,如果想深入研究请仔细阅读api文档或jdk source code

非公平锁模版方法 - acquire

模版方法的核心思想是就是基类定义好实现该功能的模版方法而不提供具体实现,通过子类能来实现不同的功能。
aquire中tryAcquire方法为模版方法默认不提供具体实现 以ReentrantLock中实现简单分析

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

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

ReentrantLock 对应代码
首先查看state的值是否为0如果为0表示锁未被其他线程占有,通过cas将state由0变为1如果成功设置持有线程为当前线程,如果state 已经被线程持有则进一步判断持有锁的线程是否是当前线程
如果是将state值增加1表示重入,此处有个技巧state+1操作并为用cas是因为当前已经获取到锁

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;
}

接下来分析下acquireQueued(addWaiter(Node.EXCLUSIVE), arg))这段代码的作用是将未获得锁的线程放入等待队列中,首先分析addWatier方法
首先尝试快速入队方式将node插入到队列尾部如果失败通过自旋+cas方式将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;
    if (pred != null) {
       node.prev = pred;
       if (compareAndSetTail(pred, node)) {
          pred.next = node;
          return node;
        }
    }
    enq(node);
    return 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;
         }
       }
  }
}

接下来看看最终方法node入队后是何控制线程的阻塞,此方法也是cas+自旋的方式处理,线程在入队后阻塞前再次判断当前线程是否能够获取到锁入果获取到锁直接返回
如果失败需要将当前线程park也就是阻塞,使用方法为LockSupport.park(this)此方法说明请看我的另一文章

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)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        return true;
    if (ws > 0) {
        do {
            node.prev = pred = pred.prev;
           } while (pred.waitStatus > 0);
           pred.next = node;
    } else {
      compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

private final boolean parkAndCheckInterrupt() {
     LockSupport.park(this);
     return Thread.interrupted();
}

释放锁 - release方法

此方法也由子类实现还是ReentrantLock进行分析,首先通过判断当前线程更持有锁线程是有一致如果不一致直接抛出异常,如果一致判断state是否为0表示锁被释放设置owner为null,因为是可重入如果不为0则将state-1后重新更新
释放锁成功后需要通知阻塞现场竞争锁,从队尾往前便利找到头节点后第一个节点并unpark

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


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;
}


private void unparkSuccessor(Node node) {
    int ws = node.waitStatus;
    if (ws < 0)
       compareAndSetWaitStatus(node, ws, 0);
    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;
    }
    if (s != null){
        LockSupport.unpark(s.thread);
    }
}

关于AQS队列的节点状态的疑问请看这篇文章
https://segmentfault.com/a/1190000022438750

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值