欢迎来到我的博客|

hu_volsnow

园龄:3年9个月粉丝:1关注:6

03-JUC-核心类AbstractQueuedSynchronizer

随便说点什么吧

下面都是有趣(无聊)的少量源码和叙述,耐心点看吧,可能会不太好懂

概念

AbstractQueuedSynchronizer 简称AQS ,从名字可以得到 synchronized是与锁相关,queue是队列的意思,也就是同步的意思。可以这样认为:AQS是一个构建锁和同步器的框架(大家都这么说,哈哈哈哈哈)

可以看到

ReentrantLock Semaphore ReentrantReadWriteLock CountDownLatch 等都用到了AQS

AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中

( CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。AQS是将每条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node)来实现锁的分配。)

读一下在源码里面的注释

Provides a framework for implementing blocking locks and related synchronizers (semaphores, events, etc) that rely on first-in-first-out (FIFO) wait queues. This class is designed to

be a useful basis for most kinds of synchronizers that rely on a single atomic {@code int} value to represent state. Subclasses must define the protected methods that change this state, and which

define what that state means in terms of this object being acquired or released. Given these, the other methods in this class carry out all queuing and blocking mechanics. Subclasses can maintain

other state fields, but only the atomically updated {@code int} value manipulated using methods {@link #getState}, {@link #setState} and {@link #compareAndSetState} is tracked with respect

to synchronization.

尝试的翻译下:

  • 这个类提供了一个框架:可以实现依赖于FIFO先进先出队列 的阻塞锁和相关同步器(比如信号量 事件等等)
  • 这个类是同步器的基础,同步依赖于一个单个的原子 int类型的变量来表示状态
  • 子类必须定义一些protected的方法来改变这个state状态,并且根据获取或者释放的对象定义改状态的含义,基于此,这个类的其他方法就是区执行同步和阻塞机制

AQS的结构

贴一部分AQS的代码

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
    protected AbstractQueuedSynchronizer() { }
    static final class Node {
        volatile int waitStatus;
        volatile Node prev;
        volatile Node next;
        volatile Thread thread;
        Node nextWaiter;
        final boolean isShared() {
            return nextWaiter == SHARED;
        }

        Node() {    // Used to establish initial head or SHARED marker
        }
        ... 
    }

    private transient volatile Node head;

    /**
     * Tail of the wait queue, lazily initialized.  Modified only via
     * method enq to add new wait node.
     */
    private transient volatile Node tail;

    /**
     * The synchronization state.
     */
    private volatile int state;
    ...// 省略
    
    
    public class ConditionObject implements Condition, java.io.Serializable {...//省略}
}

可以看出,AQS,类似于一个双向链表结构 ,由 state head部Node tail部Node 内部类Node ,还有一个内部类ConditionObject组成(用来结合锁实现线程同步),其中,Node类里面包含:waitStatus 前置节点prev 后置节点next nextWaiter thread。

其实可以这样理解,AQS是一个双向队列结构,队列里面的元素就是Node,然后通过head 和tail节点记录头部节点和尾部节点连接,可以得到如下的图

再来分析AQS为什么 是锁和同步器的框架的

AQS通过是volatitle 和CAS来实现了锁和同步

那么这个流程是怎么样的,下面来一一阐述

前面提到了AQS 的组成,有一个是说类似于双向链表的结构,有头部 尾部 状态变了state,还有链表元素Node,然后还有一个ConditionObject的内部类,这个内部类是一个条件变量,在AQS中,这个条件变量其实对应一个条件队列(单链表的),这个条件队列起到的作用就是结合锁来实现线程的同步,这个条件变量实际上是来存放调用条件变量await方法被阻塞之后的线程

AQS实现同步的关键是依赖state状态变量,一般流程是:获取到锁的线程会把state变量加1,当state=0时,表示这个线程可以去获取锁,当state的值大于1时,表示锁被占用,需要其他线程释放锁之后,并且将state置为0后,线程才可能获取到锁

然后下面说说这些队列又是咋回事

其实就是未获得锁的线程怎么办呢,这些未获得锁的线程会加入到AQS的等待队列中,靠近header的元素表示等待的时间越久,一般情况下,在某一个线程释放锁后,header元素是最有可能获得锁的那位

,在一个线程被释放锁并且调用了条件变量的await方法后,该线程就会被加入到条件队列中,进行等待,只有获得锁的线程再次释放锁的时候,在等待队列中的线程才可能重新加入到AQS等待队列中

AQS源码

在这个AQS中,最重要的就是多线程的前提下,各个线程获取资源,和释放资源,以实现锁和同步

线程获取资源的方式可以分为两种,一种是独占模式,一种是共享模式

独占模式获取资源:

public final void acquire(int arg) {
    if (!tryAcquire(arg) //<1>
        &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg)//<5>) //<2>
        selfInterrupt();
}

<1>其中tryAcquire一般由AQS的子类自定义实现,首先是判断tryAcquire是否为true,

看一下ReentrantLock中tryAcquire(公平锁的tryAcquire)实现方式:

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (!hasQueuedPredecessors()   //<3>
            &&
            compareAndSetState(0, acquires)) { //<4>
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}
//下面是hasQueuedPredecessors 
public final boolean hasQueuedPredecessors() { 
        // The correctness of this depends on head being initialized
        // before tail and on head.next being accurate if the current
        // thread is first in queue.
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

 

<2>acquireQueued 在AQS的等待队列中后,等待队列中线程也会自旋地尝试获取锁

<3> 判断是否有任何线程正在等待获取的时间长于当前线程,如果为false(等待时间没有长于当前线程的)

<4> 再进行compareAndSetState,就是进行比较state的值,如果state的值为0,那么就将state的设置值为1(这个compareAndSetState实际上是调用了unsafe#compareAndSwapInt方法)

<5>addWaiter(Node.EXCLUSIVE):如果tryAcquire返回为false,即没有获得state的操作权,就将该线程转化为Node的形式添加到等待队列中:

如果末尾tail的Node不为null,那么就将当前的线程添加到tail末尾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);  //<6>
        return node;
}

<6> enq(node)也表示入队,添加到等待队列中,如果tail元素为null,就新建一个head头部元素,并且tail=head

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

简单画一张图

 

独占模式下释放资源:

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

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

<1> 尝试释放资源tryRelease,还是以ReentrantLock中的tryRelease 为例,这个tryRelease也就是设置state的值为0

<2>然后调用LockSupport.unpark(s.thread);激活队列里面被阻塞的线程,被激活的线程尝试获取资源

 

下面通过一个例子来尝试解释一下上面的流程,借助ReentrantLock为例子

public class AbstractQueuedSyncDemo {
    public static void main(String[] args) {
        Lock lock = new ReentrantLock(); //默认是非公平锁模式
        for(int i=0; i<20;i++){
            MyThread t =  new MyThread("t"+i,lock);
            t.start();
        }
    }
}

class MyThread extends Thread{
    private Lock lock;
    public MyThread(String name,Lock lock){
        super(name);
        this.lock = lock;
    }
    
    @Override
    public void run() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() +"运行。。。。");
        } finally {
            lock.unlock();
        }
    }
}
  1. 这些线程都共用了一把锁lock,当某一个线程获得锁的时候,其他没有获得锁的线程被加入到AQS等待队列中
  2. 等调用lock.unlock之后,也就是释放了自己的锁之后,后面等待的线程会去获得锁
  1. 对于lock.lock() lock.unlock 代码片段过程(加锁,和释放锁)可以理解为使用了一个synchronized包含的代码块过程(通过内置的监视器锁实现,利用monitorEnter moniterExit指令实现)

运行结果:每次运行的结果顺序可能是不一样的

t0运行。。。。
t1运行。。。。
t4运行。。。。
t5运行。。。。
t9运行。。。。
t2运行。。。。
t8运行。。。。
t7运行。。。。
t3运行。。。。
t6运行。。。。

AQS-条件变量Condition

前面说了,在AQS结构里面,还有一个内部类ConditionObject ,这是一个条件变量,一个条件变量实际上对应的是一个单链表结构,它是在条件变量调用await方法后,会将当前线程加入到这个单链表的队列中。查看下面的例子:

public class AQSConditionDemo {

    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();
        Thread t = new Thread(() -> {
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " awaiting .....start");
                condition.await();
                System.out.println(Thread.currentThread().getName() + "  awaiting.....end");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }


        });
        Thread t2 = new Thread(() -> {
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " signal......start");
                condition.signal();
                System.out.println(Thread.currentThread().getName() + " signal...end");
            } finally {
                lock.unlock();
            }
        });
        t.start();
        t2.start();
    }
}

运行结果:

Thread-0 awaiting .....start
Thread-1 signal......start
Thread-1 signal...end
Thread-0  awaiting.....end
  1. 当线程调用await方法时,会在内部创建一个Node.Condition的node节点,addConditionWaiter方法:添加一个Node.CONDITION的node节点到条件队列的末尾
  2. 当前线程释放锁,返回一个state变量,该线程被阻塞
public final void await() throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            Node node = addConditionWaiter();// <1>
            int savedState = fullyRelease(node);
            int interruptMode = 0;
            while (!isOnSyncQueue(node)) {
                LockSupport.park(this);
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
 }
// addConditionWaiter方法:添加一个Node.CONDITION的node节点到条件队列的末尾
private Node addConditionWaiter() {
            Node t = lastWaiter;
            // If lastWaiter is cancelled, clean out.
            if (t != null && t.waitStatus != Node.CONDITION) {
                unlinkCancelledWaiters();
                t = lastWaiter;
            }
            Node node = new Node(Thread.currentThread(), Node.CONDITION);
            if (t == null)
                firstWaiter = node;
            else
                t.nextWaiter = node;
            lastWaiter = node;
            return node;
}
  1. 当另外一个线程t2调用signal方法时,doSigal(first),也就是激活在条件队列中的第一个node元素
public final void signal() {
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    Node first = firstWaiter;
    if (first != null)
         doSignal(first);
}
//doSiganl 
private void doSignal(Node first) {
    do {
        if ( (firstWaiter = first.nextWaiter) == null)
            lastWaiter = null;
        first.nextWaiter = null;
    } while (!transferForSignal(first) &&
             (first = firstWaiter) != null);
}
  1. 然后将条件队列被剔除的第一个元素node,加入到AQS队列中:enq(node)
final boolean transferForSignal(Node node) {
        /*
         * If cannot change waitStatus, the node has been cancelled.
         */
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;

        /*
         * Splice onto queue and try to set waitStatus of predecessor to
         * indicate that thread is (probably) waiting. If cancelled or
         * attempt to set waitStatus fails, wake up to resync (in which
         * case the waitStatus can be transiently and harmlessly wrong).
         */
        Node p = enq(node);
        int ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
}

注:一个锁对应一个AQS阻塞队列,但是一般可以对应多个条件队列

本文作者:hu_volsnow

本文链接:https://www.cnblogs.com/volsnow/p/15768163.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   hu_volsnow  阅读(29)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起