AQS 동기 소스 코드 분석

머리말

Github에서 : https://github.com/yihonglei/thinking-in-concurrent

큐 동기

1. 개요   

    AbstractQueuedSynchronizer의 (큐 동기화)을 싱크로 나이저가 어셈블리베이스 프레임 또는 다른 동기화 잠금을 구축하는데 사용된다라고

내부 변수를 통해 동기화 상태를 나타냅니다 (공유 리소스 대신) 휘발성 INT 상태를 유지 내장 FIFO 큐

자원 인수 완료 작업 대기열 스레드 (이 큐를 입력합니다 멀티 스레드 리소스 경합이 차단됩니다).

    동기화가 주요 사용은, 상속 동기화를 통해 서브 클래스를 상속 및 동기화 상태를 관리 할 수있는 자사의 추상 메소드를 구현되어, 전형적인 템플릿 메소드 디자인 패턴입니다,

상태가 스레드 안전 작업을 수행하기 위해 주로의 getState (), setState를 (INT newState)와 compareAndSetState을 통해 (INT 업데이트를 기대 INT).

어떤 인터페이스 자체 동기화를 구현하지 않는 정적 내부 동기화 구성 요소 클래스의 서브 클래스로 정의되어, 단순히 동기화 상태의 숫자 지원에 공유 할 수있는 정의

당신은 쉽게 동기화 구성 요소의 다른 유형, 같은 해, CountDownLatch 같은 ReentrantLock와 방법을 구현할 수 있도록 동기화 상태를 취득.

2, AQS는 두 개의 자원 공유를 정의

1) 단독은 (단독은 한 스레드 만) 등 ReentrantLock와 같이 수행 할 수있다.

2) 공유 (공동 다중 스레드는 세마포어 CountDownLatch를)으로 동시에 실행될 수있다.

두 큐 동기화 방법

1 템플릿 방법

보이드 취득 (INT ARG)

현재 스레드의 동기화 상태가 성공적으로 구입 한 경우 [독점]를, 그렇지 않으면 동기화 된 입력을 대기 큐에, 메소드가 리턴에 의해, 상태를 동기화 얻을,

이 방법은없는 tryAcquire (INT ARG) 메소드가 다시 호출됩니다.

보이드있는 acquireInterruptibly (INT ARG)

[단독] 획득 (INT) 동일하지만, 스레드 동기화 상태 취득 동기화 큐를 입력하지 않는 인터럽트에 응답하여, 행에있어서, 현재의 스레드가 중단되면

방법은 예외 : InterruptedException과 수익을 발생합니다.

부울 tryAcquireNanos (INT 인수, 긴 nanosTimeout)

[독점 같이, 스레드가 동기화가 제한 시간 내에 상태에서 획득되지 않으면있는 acquireInterruptibly (INT ARG) 방법에 기초하여 상기 시간 제한을 증가시키기

그것은 구입 한 경우는 true를 반환 false를 돌려줍니다.

보이드 acquireShard (INT ARG)

현재 스레드 동기화되지 획득 상태, 전용의 인수와의 주요 차이점을 입력 대기 동기화 큐 않는 경우 [공유하고, 동기화 된 상태를 얻을

동시에 여러 개의 스레드가 동기화 얻을 수 있습니다.

보이드 acquireSharedInterruptibly (INT ARG)

[그리고 상기 방법 인터럽트에 응답하여, acquiredShared INT (ARG) 동일한 공유.

부울 tryAcquireSharedNanos (INT 인수, 긴 nanosTimeout)

[공유]는 acquiredSharedInterruptibly에서 시간 제한 (INT ARG) 방식을 증가시킨다.

부울 릴리스 (INT ARG)

[단독] 동기 상태 이후에 릴리스된다 릴리스 동기화 상태는, 동기 스레드 큐는 제 1 노드 웨이크 업을 포함한다.

부울 releaseShared (INT ARG)

[공유] 릴리스 동기화 상태.

컬렉션 <실> 해, getQueuedThreads ()

동기화 큐에 대기중인 스레드의 컬렉션을 가져옵니다.

도 2에있어서, 재기록

된 tryAcquire 부울 보호 (INT ARG)

[단독] 획득 동기화 상태는, 상기 방법을 구현하는 것은 현재 상태를 조회 할 필요 및 기호 예상 동기 상태 여부를 판단하고 동기 상태 CAS를 설정.

tryRelease 부울 보호 (INT ARG)

[독점]를 동기화 얻을 수있는 기회를 갖게됩니다 스레드의 취득 동기화 상태로 대기, 동기 상태를 발표했다.

보호 INT tryAcquireShared (INT ARG)

[공유] 성공을 나타내는 0에서와 동일하거나보다 취득 동기화 상태, 반환 값보다, 그렇지 않으면 실패 인수합니다.

tryReleaseShared 부울 보호 (INT ARG)

[공유] 릴리스 동기화 상태.

) (isHeIdExclusively 부울 보호

현재의 thread가 독점 모드에서 동기화를 보유 여부, 프로세스는 일반적으로 현재 스레드의 독점 여부를 나타냅니다.

세 큐 동기 소스 분석

1 동기화 큐

    내부 큐 동기화 종속 동기 ( 연결리스트 구현에 기초하여 양방향 FIFO 큐 ), 현재 스레드 동기화 실패 상태 얻을 완전한 동기화 상태로 관리

현재 스레드가 차단 하겠지만 싱크로 현재 스레드는 (노드)의 노드로 구성되고 동기화 큐의 말미에 추가 상태 정보를 기다린다

동기 상태가 해제되면, 다음 노드가 깨어 나면 스레드에서 노드를 머리를 다시 동기화 상태를 얻기 위해 노력할 것입니다, 머리 노드는 존재하지 않는 스레드입니다.

    동기화 큐 노드 (노드) 동기 상태를 저장 스레드 참조를 획득는 선행 및 후속 노드처럼, 대기 상태, 실패 및

노드는 동기화 큐의 기초를 구성한다.

1.1 정적 내부 클래스 소스 노드 AQS

static final class Node {
        /** Marker to indicate a node is waiting in shared mode */
        static final Node SHARED = new Node();
        /** Marker to indicate a node is waiting in exclusive mode */
        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;

        /**
         * Status field, taking on only the values:
         *   SIGNAL:     The successor of this node is (or will soon be)
         *               blocked (via park), so the current node must
         *               unpark its successor when it releases or
         *               cancels. To avoid races, acquire methods must
         *               first indicate they need a signal,
         *               then retry the atomic acquire, and then,
         *               on failure, block.
         *   CANCELLED:  This node is cancelled due to timeout or interrupt.
         *               Nodes never leave this state. In particular,
         *               a thread with cancelled node never again blocks.
         *   CONDITION:  This node is currently on a condition queue.
         *               It will not be used as a sync queue node
         *               until transferred, at which time the status
         *               will be set to 0. (Use of this value here has
         *               nothing to do with the other uses of the
         *               field, but simplifies mechanics.)
         *   PROPAGATE:  A releaseShared should be propagated to other
         *               nodes. This is set (for head node only) in
         *               doReleaseShared to ensure propagation
         *               continues, even if other operations have
         *               since intervened.
         *   0:          None of the above
         *
         * The values are arranged numerically to simplify use.
         * Non-negative values mean that a node doesn't need to
         * signal. So, most code doesn't need to check for particular
         * values, just for sign.
         *
         * The field is initialized to 0 for normal sync nodes, and
         * CONDITION for condition nodes.  It is modified using CAS
         * (or when possible, unconditional volatile writes).
         */
        // 节点状态,直接影响线程唤醒
        volatile int waitStatus;

        /**
         * Link to predecessor node that current node/thread relies on
         * for checking waitStatus. Assigned during enqueuing, and nulled
         * out (for sake of GC) only upon dequeuing.  Also, upon
         * cancellation of a predecessor, we short-circuit while
         * finding a non-cancelled one, which will always exist
         * because the head node is never cancelled: A node becomes
         * 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;

        /**
         * Link to the successor node that the current node/thread
         * unparks upon release. Assigned during enqueuing, adjusted
         * when bypassing cancelled predecessors, and nulled out (for
         * sake of GC) when dequeued.  The enq operation does not
         * assign next field of a predecessor until after attachment,
         * so seeing a null next field does not necessarily mean that
         * node is at end of queue. However, if a next field appears
         * to be null, we can scan prev's from the tail to
         * double-check.  The next field of cancelled nodes is set to
         * point to the node itself instead of null, to make life
         * easier for isOnSyncQueue.
         */
        // 下一个节点
        volatile Node next;

        /**
         * The thread that enqueued this node.  Initialized on
         * construction and nulled out after use.
         */
        // 获取同步状态的线程,被包装在Node节点中,放在阻塞队列里
        volatile Thread thread;

        /**
         * Link to next node waiting on condition, or the special
         * value SHARED.  Because condition queues are accessed only
         * when holding in exclusive mode, we just need a simple
         * linked queue to hold nodes while they are waiting on
         * conditions. They are then transferred to the queue to
         * re-acquire. And because conditions can only be exclusive,
         * we save a field by using special value to indicate shared
         * mode.
         */
        // 下一个等待唤醒的节点
        Node nextWaiter;

        /**
         * Returns true if node is waiting in shared mode.
         * 判断是否是共享模式
         */
        final boolean isShared() {
            return nextWaiter == SHARED;
        }

        /**
         * Returns previous node, or throws NullPointerException if null.
         * Use when predecessor cannot be null.  The null check could
         * be elided, but is present to help the VM.
         *
         * @return the predecessor of this node
         */
        // 返回上一个节点
        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;
        }
    }

1.2 소스 노드 정보

INT waitStatus (대기 상태) :

1) waitStatus 0의 디폴트 값으로, INT, 초기 상태의 값이 0이고;

2) 취소 1의 값, 또는 밖으로 동기 대기 큐에 대기중인 스레드가 인터럽트되고,이 동기 대기 행렬을 취소 할 필요가 있고,

노드는 상태 변화하지 않는다 들어간다

3) SIGNAL -1, 대기 상태 및 스레드 선행 노드 스레드 후임 노드 박리 상태 또는 동기화가 취소되면

우리는 다음 노드를 실행하려면 노드의 후속 스레드를 통지합니다;

4) CONDITION, 대기 큐 노드 -2 값 상태의 스레드에 대한 노드가 대기 상태시 조건 () 메소드에 대한 다른 스레드에 대한 호출 신호는

동기화 큐에 대기 행렬로 전송할 것이다 노드가 동기화 상태의 취득에 첨가 하였다;

5) 전파는 -3이고 동기 획득 공유 다음 상태 무조건 다운 전파한다 나타낸다

노드 이전 (전구체 노드) :

노드 동기화 큐 (꼬리 첨가) 세트에 추가 될 때 선행 노드.

다음 노드 (후임 노드) :

후계자 노드입니다.

노드 nextWaiter (후속 노드가 큐 대기) :

큐 후임 노드를 대기. 현재 노드를 공유하는 경우, 필드는 상수 SHARED 될 것이다 수단 (전용 및 공유) 노드 유형

그리고 하나의 후속 노드 필드 큐에서 대기.

스레드 스레드 (스레드) :

 스레드 동기화 상태를 가져옵니다.

동기 큐 1.3의 기본 구조

노드가 일치 큐 기초, 동기화는 노드의 구성 것이다 스레드 동기화를 획득하기 위해 제 성공없이 노드 (헤드)과 노드 꼬리 (테일)를 가지고

큐의 말미에 부가하고,도 2의 기본 구조는, 동기 대기 행렬을 따른다.

싱크로 나이저 (자바, 목록이 참조에 의해 달성된다) 개의 기준 노드 형태를 포함하는 헤드 노드에 대한 포인터, 테일 노드는 다른 포인팅.

스레드가 성공적으로 얻어지는 동기 상태 (또는 고정)는 다른 스레드 동기화 상태를 취득 할 수없는 경우, 결과적으로,이 노드 동기화 큐에 추가하도록 구성되는,

이 처리는 실의 안전을 보장 큐에 추가되어야하며, 따라서, 동기화는 노드 CAS의 설정에 따라 동작하는 방법을 제공한다 :

compareAndSetTail (노드는 노드 업데이트, 기대)

단지 공식적으로 이전 노드의 종료와 관련된 현재 노드의 성공 후 설정 한 현재의 thread "이"꼬리 노드와 현재 노드를 전달.

1.4 동기 큐 노드 가하고

 큐의 꼬리 노드를 추가 할 수 있습니다.

1.5 노드 집합

동기식 FIFO가 첫 번째 노드의 원칙이 성공적으로 동기화 상태 노드 취득입니다 따라 대기, 발표 스레드 동기화 상태의 첫 번째 노드의 후속 노드를 깨울 것이다,

후속 노드는 동기 상태를 얻기에 성공 자체가 헤드 노드를 설정한다.

하나의 스레드 만 충분히 성공은 그래서 헤드 노드 방법을 설정, 동기화 상태를 얻을 수 있기 때문에 첫 번째 노드가 성공적으로 실을 완료 동기화 상태를 취득하여 설정

그것은 첫 번째 노드가 첫 번째 노드의 원래 노드 첫 번째 노드의 후계자가 될 설정하고 옆에있는 원래의 참조가 분리되어 필요로하기 위해 CAS를 사용할 필요가 없습니다.

(2) 동기 상태 취득 배타적 릴리스

2.1 동기 상태 취득 배타적 소스 코드 분석

이 방법은 공유 자원 독점 모드 스레드가 상단 입구를 얻을 수있다. 그렇지 않으면 대기 큐를 입력, 직접 다시 스레드 자원을 점점 후,

획득 자원까지 날짜 및 전체 프로세스에 중단의 영향을 무시한다. 이것은 정확히 잠금 ()의 의미는 물론, 잠금에 국한되지 ().

자원을 획득 한 후에는 임계 영역의 코드를 실행 스레드로 이동할 수 있습니다.

public final void acquire(int arg) {
    // 尝试获取同步状态
    if (!tryAcquire(arg) &&
        // addWaiter构建节点,并加入队尾,acquireQueued根据当前节点尝试获取同步状态,
        // 获取失败,则阻塞,如果成功,则返回
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        // 获取锁失败,标记线程状态为中断状态
        selfInterrupt();
}

된 tryAcquire (INT ARG)

먼저, 된 tryAcquire 동기화 된 상태로 (INT ARG) 메소드 스레드 안전한 액세스를 구현 사용자 정의 동기화를 호출, 인수가 성공적으로 반환에 해당하는 경우,

그렇지 않으면 동기화 상태 획득은, 반환 거짓 실패;

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

이 방법은 AQS의 재기록 방법, 적절한 논리를 구현하는 하위 클래스입니다.

addWaiter (노드 모드)

당신이 동기화 상태가 실패받을 경우, acquireQueued ()를 호출 addWaiter (Node.EXCLUSIVE를 인수)),

먼저, addWaiter (Node.EXCLUSIVE), 구성 동기화 노드 (전용 Node.EXCLUSIVE를 호출

동시에는 하나의 스레드 만이 성공적으로 획득 동기화 상태)와, addWaiter (노드 모드)는 현재 노드의 스레딩 방법에 의해 대기 큐의 끝에 추가 될

그리고 현재의 thread의 노드를 반환합니다.

private Node addWaiter(Node mode) {
    // 根据给定模式构造节点,mode有两种:EXCLUSIVE(独占式)和SHARED(共享式)
    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)来完成初始化和入队操作
    enq(node);
    // 返回当前线程所在的节点
    return node;
}

ENQ (노드 노드)

private Node enq(final Node node) {
    // CAS自旋,直到成功加入队尾
    for (;;) {
        Node t = tail;
        // 队列为空,创建一个空的标志节点作为head节点,并将tail也指向它
        if (t == null) { // Must initialize
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            // 正常流程,放入队尾,这个时候队列有两个元素,一个是new Node()头结点
            // 另外一个是基于获取同步状态构建的Node节点
            node.prev = t;
            if (compareAndSetTail(t, node)) {
                t.next = node;
                return t;
            }
        }
    }
}

은 "죽은 루프"를 통해 동기화가 올바른 노드는 CAS 노드 설정에 의해 꼬리 노드 될 것이다 "죽음의 사이클"후, 추가되었는지 확인하기 위해, 현재의 thread 방법에서 돌아갑니다,

그렇지 않으면, 현재의 thread를 설정하려고하는 것을 계속한다. 

acquireQueued (최종 노드 노드 INT ARG)

된 tryAcquire ()와 addWaiter ()으로 자원에 대한 스레드 액세스 실패, 대기 큐 꼬리에 배치되었습니다.

acquireQueued ()가 자원에 대한 대기열 대기열 액세스에서 호출 스레드에 대한 책임이 일시 정지, 리소스에 대한 액세스가 돌아올 때까지.

l boolean acquireQueued(final Node node, int arg) {
    // 标记是否获取资源失败,默认true获取失败
    boolean failed = true;
    try {
        // 标记等待过程中是否被中断过
        boolean interrupted = false;
        // CAS自旋
        for (;;) {
            /*
             * 如果前驱是head,即当前节点已变成第二个节点,那么便有资格去尝试获取资源
             *(可能是前驱节点释放完资源唤醒自己的,当然也可能被interrupt了)。
             */
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                /*
                 * 拿到资源后,将自己设置成为head,同时断掉原head的链接p.next=null;让GC回收。
                 */
                setHead(node);
                // jvm采用可达性分析算法判定对象是否可以被回收,让对象没有引用,使jvm回收
                p.next = null; // help GC
                // 获取锁成功,failed设置为false,finally的cancelAcquire取消获取锁逻辑就不需要走
                failed = false;
                // 返回等待过程中的中断状态
                return interrupted;
            }
            // shouldParkAfterFailedAcquire修改头节点状态成功后通过 
            // parkAndCheckInterrupt方法的LockSupport.park(this)暂停线程
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                // 如果等待过程中被中断过,就将interrupted标记为true
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

shouldParkAfterFailedAcquire (노드, 노드)

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    // 前驱节点状态,直接决定要不要进行后继节点唤醒,让后继节点去尝试获取同步状态
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        /*
         * This node has already set status asking a release
         * to signal it, so it can safely park.
         *
         * 如果当前节点已经告诉前驱节点进行唤醒,自己就可以安心停止。
         */
        return true;
    if (ws > 0) {
        /*
         * Predecessor was cancelled. Skip over predecessors and
         * indicate retry.
         * 
         * 如果前驱放弃了,那就一直往前找,直到找到最近一个正常等待的状态,并排在它的后边。
         * 注意:那些放弃的节点,它们相当于形成一个无引用链,在GC时会被回收。
         */
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        /*
         * waitStatus must be 0 or PROPAGATE.  Indicate that we
         * need a signal, but don't park yet.  Caller will need to
         * retry to make sure it cannot acquire before parking.
         *
         * 如果前驱正常,那就把前驱的状态设置成SIGNAL,以便唤醒后继节点。
         */
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

parkAndCheckInterrupt ()

private final boolean parkAndCheckInterrupt() {
    // 暂停线程
    LockSupport.park(this);
    // 返回线程的中断状态
    return Thread.interrupted();
}

공원 ()는 대기 상태로 현재의 thread에 있습니다. 이 상태에서, 당신은 스레드를 깨울 수있는 두 가지 방법이 있습니다 :

1)) (언 파킹이고

2) 인터럽트 ()

요약 :

취득은 (INT ARG) 과정을 호출

 selfInterrupt ()

static void selfInterrupt() {
    Thread.currentThread().interrupt();
}

인터럽트 작업을 스레드.

2.2 독점 릴리스 동기화 상태

릴리스 (INT ARG)

이 방법은 최상위 입구 공유 리소스를 해제하기 위해 단독 모드 스레드입니다. 완전히 (즉, 상태 = 0)를 출시하는 경우는, 자원의 특정 금액을 발표 할 예정이다

그것은 자원을 얻기 위해 대기열에 대기중인 다른 스레드를 깨워 것입니다. 이것은 물론 잠금 해제에 국한하지의 정확히 () 의미 잠금 해제 ()이다.

public final boolean release(int arg) {
    // 如果获取锁成功,才会去处理线程的唤醒
    if (tryRelease(arg)) {
        // 找到头节点
        Node h = head;
        if (h != null && h.waitStatus != 0)
            // 唤醒等待队列里的下一个线程
            unparkSuccessor(h);
        return true;
    }
    return false;
}

그것은 tryRelease ()가 리소스를 해제하기 위해 호출합니다. 주의 할 점은이 스레드가 완성 된 버전이 있는지 여부를 결정하기 위해 tryRelease () 반환 값을 기반으로한다는 것입니다

자원의 가자! 그래서 사용자 정의 동기화 tryRelease ()이 명확하게하고자 할 때의 디자인!

tryRelease (INT ARG)

이 방법은 자원의 지정 수량을 해제하려고 시도합니다.

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

된 tryAcquire ()와 같은,이 방법은 달성 독점 모드 지정 싱크로 나이저가 필요하다. 일반적으로, tryRelease ()는 성공한다

이 독점 모드이기 때문에, 스레드, 그것은 독점적 인 자원을 제공되어 있어야합니다 리소스를 해제하고, 손실 자원의 적절한 양 (상태 - = ARG)을 직접,

필요가 스레드 안전 문제를 고려 없습니다. 그러나 이미 위에서 언급 한 반환 값, 관심을 지불, 릴리스 () () 값은 tryRelease 반환을 결정이다

이 스레드가 자원을 해방을 완료 한 경우! 완전히 자원 (상태 = 0)를 해제해야하는 경우 독선적 세트 그래서 true, 그렇지 않은 경우는 false 반환, 싱크로 나이저를 구현할 때.

unparkSuccessor (노드 노드)

이 방법을 사용하면 잠금이 해제 완료 할 때 동기화 얻을하려고 눈을 뜨고 다른 사람에게, 다음 큐를 기다리는 스레드를 깨워하는 데 사용되며 엉덩이 휴가를 이길 수 없습니다.

private void unparkSuccessor(Node node) {
        /*
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling.  It is OK if this
         * fails or if status is changed by waiting thread.
         */
        // node一般为当前线程所在的节点
        int ws = node.waitStatus;
        if (ws < 0)
            // 置零当前线程所在的节点状态,允许失败
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * Thread to unpark is held in successor, which is normally
         * just the next node.  But if cancelled or apparently null,
         * traverse backwards from tail to find the actual
         * non-cancelled successor.
         */
        // 找到下一个需要唤醒的节点s
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {// 如果为空或已取消
            s = null;
            // 从这里可以看出,<=0的节点,都是还有效的节点
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            // 唤醒
            LockSupport.unpark(s.thread);
    }

언 파킹을 통해 () 헤드 노드에서 큐의 뒷면을 포기하지 않는 대기 스레드를 깨워, 우리가 그것을 표현하기 위해 S를 사용합니다. 이 시점에서, 다시 acquireQueued () 링크,

(p == 헤드가 된 tryAcquire && (ARG)) 판정되면 웨이크 진행 한 후 S (= 헤드없이, 그 shouldParkAfterFailedAcquire를 다시 입력한다 없더라도 P! ()

안전한 지점을 찾고. 이제 여기에 이미 대부분의 스레드를 포기하지 않았다, 다음 shouldParkAfterFailedAcquire에 의해 () 조정의 앞에 큐에서 대기중인 S,

(가) 극 헤드 노드를이야로 머리가 불가피 다음 노드로 이동합니다 S, P의 설립에 다음 스핀 == 머리)이며, 다음 자신을 설정, 그는 인수했다고 밝혔다

자원, 획득 ()도 반환!

3, 공유 액세스 및 자료 동기화 상태

3.1 동기 상태 주 취득

acquireShared (INT ARG)

이 방법은 공유 모드 스레드가 최상위 항목의 자원을 공유됩니다된다. 그것은 성공적으로 직접 반환에 접근, 실패가 입력 대기 큐를 얻기 위해, 자원의 특정 금액을 얻을 것이다

인터럽트를 무시하고 전체 프로세스까지 자원을 얻기까지.

public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)
       doAcquireShared(arg);
}

tryAcquireShared (INT ARG)

protected int tryAcquireShared(int arg) {
    throw new UnsupportedOperationException();
}

취득 리소스에 tryAcquireShared () 시도, 성공은 직접 반환됩니다.

대기 큐에 doAcquireShared에 의한 고장 (), 자원을 반환하기 전에 지금까지 얻을 때까지.

doAcquireShared (INT ARG)

이 방법은 자신을 자각하는 리소스를 해제하기 위해 다른 스레드 때까지 일시 중단되는 현재의 thread에 가입 큐 대기의 꼬리를 사용, 그들의 성공은 반환하기 전에 자원의 적절한 양을 얻을 수 있습니다. 

private void doAcquireShared(int arg) {
    // 加入队列尾部
    final Node node = addWaiter(Node.SHARED);
    // 获取锁是否失败标识
    boolean failed = true;
    try {
        // 等待过程中是否被中断过的标志
        boolean interrupted = false;
        for (;;) {
            // 获取前驱节点
            final Node p = node.predecessor();
            // 如果到head的下一个,因为head是拿到资源的线程,此时node被唤醒,很可能是head用完资源来唤醒自己的
            if (p == head) {
                // 尝试获取资源
                int r = tryAcquireShared(arg);
                if (r >= 0) {// 成功
                    // 将head指向自己,还有剩余资源可以再唤醒之后的线程
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    if (interrupted)// 如果等待过程中被打断过,此时将中断补上
                        selfInterrupt();
                    failed = false;
                    return;
                }
            }
            // 判断状态,寻找安全点,进入waiting状态,等着被unpark()或interrupt()
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

setHeadAndPropagate (노드 노드, INT의 전파)

private void setHeadAndPropagate(Node node, int propagate) {
    Node h = head; // Record old head for check below
    // head指向自己
    setHead(node);
    /*
     * Try to signal next queued node if:
     *   Propagation was indicated by caller,
     *     or was recorded (as h.waitStatus either before
     *     or after setHead) by a previous operation
     *     (note: this uses sign-check of waitStatus because
     *      PROPAGATE status may transition to SIGNAL.)
     * and
     *   The next node is waiting in shared mode,
     *     or we don't know, because it appears null
     *
     * The conservatism in both of these checks may cause
     * unnecessary wake-ups, but only when there are multiple
     * racing acquires/releases, so most need signals now or s
     * anyway.
     */
    // 如果还有剩余量,继续唤醒下一个邻居线程
    if (propagate > 0 || h == null || h.waitStatus < 0 ||
        (h = head) == null || h.waitStatus < 0) {
        Node s = node.next;
        if (s == null || s.isShared())
            doReleaseShared();
    }
}

조건이 충족되면 setHead에 따라이 다단 방식 ()에, 동시에 그 여파가되고,뿐만 아니라, 각성의 후속 노드로 (예를 들어, 리소스의 잉여가)

모든 후, 모드 공유!

doReleaseShared ()

private void doReleaseShared() {
    /*
     * Ensure that a release propagates, even if there are other
     * in-progress acquires/releases.  This proceeds in the usual
     * way of trying to unparkSuccessor of head if it needs
     * signal. But if it does not, status is set to PROPAGATE to
     * ensure that upon release, propagation continues.
     * Additionally, we must loop in case a new node is added
     * while we are doing this. Also, unlike other uses of
     * unparkSuccessor, we need to know if CAS to reset status
     * fails, if so rechecking.
     */
    for (;;) {
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;            // loop to recheck cases
                // 唤醒后继
                unparkSuccessor(h);
            }
            // head发生变化
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;                // loop on failed CAS
        }
        if (h == head)                   // loop if head changed
            break;
    }
}

 

3.2 공유 동기 상태 해제

releaseShared (INT ARG)

이 방법은 릴리스 최상위 입구 공유 리소스에 모드 스레드를 공유됩니다. 성공적으로 출시하고 대기중인 스레드를 깨울 수 있다면 그것은, 자원의 특정 금액을 발표 할 예정이다

그것은 자원을 얻기 위해 대기열에 대기중인 다른 스레드를 깨워 것입니다. 

public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}

전용 모드에서 tryRelease () 후 완전히 안심 자원 (상태 = 0), 주로 독점 재진입 고려 기반으로 다른 스레드를 깨워에 true를 돌려줍니다;

공유 모드에서의 releaseShared ()는 더 본질적으로 같은 요구 사항, 공유 제어 모드를 동시에 실행되는 스레드의 일정 금액, 해제 자원과 스레드가 없습니다

후속 노드가 어떤 자원을 깨워 위해 우리는 기다릴 수 있습니다. 예를 들어, 자원의 총량을 C (4) (13), (5), B (7)이다 실행 동시에 취득한 리소스 때 하나만

자원 기다려야 할 것이다. 두 자원이 작동 중에 해제 한 다음 tryReleaseShared (2)가 true 웨이크 C 반환, C는 여전히 기다리게하지 않는 경우에만 세를 보았다

B 후 2, tryReleaseShared (2) 해제 사실 웨이크-C는 C와 함께 5 채우기 스스로를 보았다 반환 한 다음 함께 A와 B와 C를 실행할 수 있습니다

ReentrantReadWriteLock 읽기 잠금 tryReleaseShared ()는 완전히 안심 자원 (상태 = 0)는 사용자 정의 동기화하므로, true를 돌려줍니다

그것은 필요 tryReleaseShared ()를 호출하는 값에 따라 결정될 수있다.

tryReleaseShared (INT ARG)

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

doReleaseShared ()

이 방법은 주로 후계자를 해제하는 데 사용됩니다.

private void doReleaseShared() {
    /*
     * Ensure that a release propagates, even if there are other
     * in-progress acquires/releases.  This proceeds in the usual
     * way of trying to unparkSuccessor of head if it needs
     * signal. But if it does not, status is set to PROPAGATE to
     * ensure that upon release, propagation continues.
     * Additionally, we must loop in case a new node is added
     * while we are doing this. Also, unlike other uses of
     * unparkSuccessor, we need to know if CAS to reset status
     * fails, if so rechecking.
     */
    for (;;) {
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;            // loop to recheck cases
                unparkSuccessor(h); // 唤醒后继
            }
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) 
                continue;                // loop on failed CAS
        }
        if (h == head)                   // loop if head changed(head发生变化)
            break;
    }
}

 

4 배타적 동기 획득 타임 아웃 상태

tryAcquireNanos (INT 인수, 긴 nanosTimeout)

독점 최상위 인터페이스 타임 아웃 상태 동기를 획득.

public final boolean tryAcquireNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    return tryAcquire(arg) ||
        doAcquireNanos(arg, nanosTimeout);
}

된 tryAcquire (INT ARG)

일반 단독 잠금은 동일한 소스를 얻을.

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

doAcquireNanos (INT 인수, 긴 nanosTimeout)

구현의 독점 취득 제한 시간.

private boolean doAcquireNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    // 如果没有传超时,则直接返回获取同步状态失败
    if (nanosTimeout <= 0L)
        return false;
    // 获取锁的截止时间,当前系统时间+传入的超时时间
    final long deadline = System.nanoTime() + nanosTimeout;
    // 构建独占锁节点
    final Node node = addWaiter(Node.EXCLUSIVE);
    // 是否成功标志
    boolean failed = true;
    try {
        // CAS自旋
        for (;;) {
            // 获取前驱节点
            final Node p = node.predecessor();
            /*
             * 如果前驱是head,即该节点已变成第二个节点,那么便有资格去尝试获取资源
             *(可能是前驱节点释放完资源唤醒自己的,当然也可能被interrupt了)。
             */
            if (p == head && tryAcquire(arg)) {
                /*
                 * 拿到资源后,将head指向该节点。所以head所指的标杆节点,
                 * 就是当前获取到资源的那个节点或null。
                 */
                setHead(node);
                // jvm采用可达性分析算法判定对象是否可以被回收,让对象没有引用,使jvm回收
                p.next = null; // help GC
                // 返回等待过程中是否被中断过
                failed = false;
                return true;
            }
            // 计算获取锁的超时时间
            nanosTimeout = deadline - System.nanoTime();
            // 如果小于等于0,说明获取锁超时,则返回获取同步状态失败
            if (nanosTimeout <= 0L)
                return false;
            // 判断状态,寻找安全点,进入waiting状态,等着被parkNanos
            if (shouldParkAfterFailedAcquire(p, node) &&
                nanosTimeout > spinForTimeoutThreshold)
                // 线程超时停止
                LockSupport.parkNanos(this, nanosTimeout);
            // 如果线程被中断过,则抛出线程中断异常
            if (Thread.interrupted())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

 

참조

앞으로 "자바 병행 프로그래밍의 예술"Fangteng 페이 웨이 Pengcheng 샤오밍;

자바 API 문서;

게시 된 502 개 원래 기사 · 원의 찬양 (358) · 조회수 1,180,000 +

추천

출처blog.csdn.net/yhl_jxy/article/details/102535590