3.14 JavaAQSの主成分分析

3.14 AQS

無敵私は戻ってきた

       AQSはJavaでのAbstractQueuedSynchronizerの略語です。AQSは非常に有名であるため、そのフルネームは忘れられがちです。AQSとは何かを見てみましょう。

       先入れ先出し(FIFO)待機キューに依存するブロッキングロックおよび関連するシンクロナイザー(セマフォ、イベントなど)を実装するためのフレームワークを提供します。このクラスは、状態を表すために単一のアトミック{@codeint}値に依存するほとんどの種類のシンクロナイザーの有用な基礎となるように設計されています。1

       AQSは、先入れ先出しの待機キューを介してロックと対応する同期を実装するためのフレームワークを提供します。AQSは、アトミック値に依存して状態を表し、同期操作の基礎を構築します。



3.14.1AQSの継承関係

       AbstractQueuedSynchronizer(AQS)はAbstractOwnableSynchronizer(AOS)を継承します。まず、AOSとは何かを見てみましょう。
ここに画像の説明を挿入


3.14.2 AbstractOwnableSynchronizer

       AOSには、現在実行中の排他スレッドを表すプライベートexclusiveOwnerThreadスレッド属性が1つだけあり、そのサブクラスは、保護されたgetメソッドとsetメソッドを介してのみこの属性にアクセスできます。

public abstract class AbstractOwnableSynchronizer
    implements java.io.Serializable {
    
    
    private static final long serialVersionUID = 3737899427754241961L;
    protected AbstractOwnableSynchronizer() {
    
     }
    private transient Thread exclusiveOwnerThread;
    protected final void setExclusiveOwnerThread(Thread thread) {
    
    
        exclusiveOwnerThread = thread;
    }
    protected final Thread getExclusiveOwnerThread() {
    
    
        return exclusiveOwnerThread;
    }
}



3.14.3 AQSCLHキュー

       CLHキューはスピンロックキューの一種ですが、スピンロックキューとはどういう意味ですか?ヘッドノードスレッドが実行されているとき、後続のノードスレッドは回転状態にあります。

ここに画像の説明を挿入

       CLHキューノードのソースコードは、実際には非常に単純で、二重にリンクされたリストです。各ノードには、スレッドと、スレッドのステータスを表すwaitStatusがあります。唯一の不揮発性の変更されたnextWaiter属性もあります。属性Nodeがこのタイプの属性のSHAREDノードである場合、それは共有を意味します。このSHAREDノードのスレッドは共有可能であり、排他性がないため、行う必要はありません。 volatileキーワードで変更します。

static final class Node {
    
    
    static final Node SHARED = new Node();
    static final Node EXCLUSIVE = null;

    /** waitStatus value to indicate thread has cancelled */
    static final int CANCELLED =  1;
    /** waitStatus value to indicate successor's thread needs unparking */
    static final int SIGNAL    = -1;
    /** waitStatus value to indicate thread is waiting on condition */
    static final int CONDITION = -2;
    /**
     * waitStatus value to indicate the next acquireShared should
     * unconditionally propagate
     */
    static final int PROPAGATE = -3;
    volatile int waitStatus;
     * head only as a result of successful acquire. A
     * cancelled thread never succeeds in acquiring, and a thread only
     * cancels itself, not any other node.
     */
    volatile Node prev;
    volatile Node next;
    volatile Thread thread;
    Node nextWaiter;
    final boolean isShared() {
    
    
        return nextWaiter == SHARED;
    }
    final Node predecessor() throws NullPointerException {
    
    
        Node p = prev;
        if (p == null)
            throw new NullPointerException();
        else
            return p;
    }

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

    Node(Thread thread, Node mode) {
    
         // Used by addWaiter
        this.nextWaiter = mode;
        this.thread = thread;
    }

    Node(Thread thread, int waitStatus) {
    
     // Used by Condition
        this.waitStatus = waitStatus;
        this.thread = thread;
    }
}

       AQSのCLHキューメカニズムは次のとおりです。
ここに画像の説明を挿入


3.14.4AQS構成

       二重にリンクされたリスト。1つは頭を指し、1つは尾を指し、さらに属性を示す状態を示します。

/**
 * Head of the wait queue, lazily initialized.  Except for
 * initialization, it is modified only via method setHead.  Note:
 * If head exists, its waitStatus is guaranteed not to be
 * CANCELLED.
 */
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;



3.14.5ソースコードメソッド分析

       CLHキューに新しいノードを追加し(テール挿入方法)、テールに新しいノードを追加します。

private Node enq(final Node node) {
    
    
	//循环直到尾节点不为null,添加到尾部
    for (;;) {
    
    
        Node t = tail;
        if (t == null) {
    
     // Must initialize
        	//当前head为null比较并更新成新Node,是Unsafe提供的方法,可以看出头Node不存线程。
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
    
    
            node.prev = t;
            if (compareAndSetTail(t, node)) {
    
    
                t.next = node;
                return t;
            }
        }
    }
}

       addWaiterメソッド

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;
    //当尾节点不为null,直接入队列
    if (pred != null) {
    
    
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
    
    
            pred.next = node;
            return node;
        }
    }
   	//当尾节点为null,循环先构建head节点然后入队列
    enq(node);
    return node;
}

       AcquisitionQueuedメソッド

final boolean acquireQueued(final Node node, int arg) {
    
    
    boolean failed = true;
    try {
    
    
        boolean interrupted = false;
        for (;;) {
    
    
            final Node p = node.predecessor();
            //tryAcquire(arg)抽象方法,子类具体实现
            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);
    }
}

3.14.6まとめ

       AQSに記載されていないメソッドがまだたくさんありますが、実際には動いていません。Javaソースコードの中で最も難しい部分として、作者もこの点を書くことを非常に心配しています。何度も読んだことがありますが、詳細はたくさんあります。理解してください。ロックを作成する前にまずAQSについて説明する必要がありますが、AQSは非常に難しいので、最初にロックを知ってからAQSを見ると理解しやすくなります。


  1. JAVAソースAQSの説明↩︎

おすすめ

転載: blog.csdn.net/weixin_44671737/article/details/114041183