AbstractQueuedSynchronizer源码分析

AbstractQueuedSynchronizer是concurrent工具包的核心抽象类,也是lock,Semaphore、CountDownLatch的基础。(CyclicBarriar内部是通过reentrantlock实现)

一.源码分析

1.继承
  • AbstractQueuedSynchronizer继承AbstractOwnableSynchronizer
public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
  • AbstractOwnableSynchronizer:是一个同步器,用来设置占用lock资源的线程
public abstract class AbstractOwnableSynchronizer
    implements java.io.Serializable {

    private static final long serialVersionUID = 3737899427754241961L;

    protected AbstractOwnableSynchronizer() { }

    /**
     * 同步占用线程.
     */
    private transient Thread exclusiveOwnerThread;

    /**
     * 设置当前拥有独占访问的线程。null参数表示没有线程拥有访问权。该方法不会强制执行任何同步或不稳定的字段访问。
     */
    protected final void setExclusiveOwnerThread(Thread t) {
        exclusiveOwnerThread = t;
    }

    /**
     * 返回由setExclusiveOwnerThread所设置的线程,如果没有设置,则返回null。该方法不会强制执行任何同步或volatile字段访问。
     */
    protected final Thread getExclusiveOwnerThread() {
        return exclusiveOwnerThread;
    }
}
2.node结构
  • node的原理:
Wait queue node class. 
等待队列节点类

The wait queue is a variant of a "CLH" (Craig, Landin, and Hagersten) lock queue. 
CLH locks are normally used for spinlocks. 
等待队列是“CLH”(克雷格、Landin和哈格斯登)的一个变体。CLH锁通常用于自旋锁。

We instead use them for blocking synchronizers, but use the same basic tactic of holding some of the control information about a thread in the predecessor of its node.
我们使用它们来阻塞同步器,但是使用相同的基本策略,在当前节点的前驱中保存一些关于线程的控制信息。


A "status" field in each node keeps track of whether a thread should block.
A node is signalled when its predecessor releases. 
Each node of the queue otherwise serves as a specific-notification-style monitor holding a single waiting thread. 

每个节点中的“status”字段可以跟踪是否应该阻塞线程。
当它的前驱释放时,当前节点会被唤醒。
队列的每个节点都充当一个特定的-notific样式监视器,它持有一个等待线程。


The status field does NOT control whether threads are granted locks etc though.
A thread may try to acquire if it is first in the queue. 
But being first does not guarantee success; 
it only gives the right to contend. 
So the currently released contender thread may need to rewait. 

状态字段不能控制线程是否被授予锁。
如果线程是队列中的第一个线程,则可能尝试获取它。
但作为第一并不保证成功;它只赋予了竞争的权利。
因此,当前发布的竞争者可能需要重新等待。

To enqueue into a CLH lock, you atomically splice it in as new tail. To dequeue, you just set the head field. 

要将其放入CLH锁中,您可以将其作为新的尾部进行拼接。对于dequeue,你只设置了head字段

      +------+  prev +-----+       +-----+
 head |      | <---- |     | <---- |     |  tail
      +------+       +-----+       +-----+
 
Insertion into a CLH queue requires only a single atomic operation on "tail", so there is a simple atomic point of demarcation from unqueued to queued. Similarly, dequeing involves only updating the "head". However, it takes a bit more work for nodes to determine who their successors are, in part to deal with possible cancellation due to timeouts and interrupts. 

插入到CLH队列中只需要在“tail”上执行一个原子操作,因此有一个简单的原子点,从不排队到排队。类似地,“弹出”只需要更新“头部”。然而,节点需要更多的工作来确定它们的后继者是谁,这在一定程度上是由于超时和中断可能导致的取消。

The "prev" links (not used in original CLH locks), are mainly needed to handle cancellation. If a node is cancelled, its successor is (normally) relinked to a non-cancelled predecessor. 
“prev”链接(不在原始的CLH锁中使用)主要是用来处理取消的。如果一个节点被取消,它的后继(通常)会与一个未被取消的前任重新联系。

For explanation of similar mechanics in the case of spin locks, see the papers by Scott and Scherer at http://www.cs.rochester.edu/u/scott/synchronization/ 

We also use "next" links to implement blocking mechanics. The thread id for each node is kept in its own node, so a predecessor signals the next node to wake up by traversing next link to determine which thread it is. Determination of successor must avoid races with newly queued nodes to set the "next" fields of their predecessors. This is solved when necessary by checking backwards from the atomically updated "tail" when a node's successor appears to be null. (Or, said differently, the next-links are an optimization so that we don't usually need a backward scan.) 

我们还使用“下一个”链接来实现阻塞机制。每个节点的线程id都保存在自己的节点中,因此,前一个节点将通过遍历下一个链接来确定下一个节点,以确定它是哪个线程。继任者的确定必须避免与新排队节点的竞争,以设置其前任的“下一个”字段。当一个节点的继任者看起来为null时,通过从原子更新的“尾部”向后检查,就可以解决这个问题。(或者,换个说法,下一个链接是一个优化,因此我们通常不需要向后扫描。)

Cancellation introduces some conservatism to the basic algorithms. Since we must poll for cancellation of other nodes, we can miss noticing whether a cancelled node is ahead or behind us. This is dealt with by always unparking successors upon cancellation, allowing them to stabilize on a new predecessor, unless we can identify an uncancelled predecessor who will carry this responsibility. 

取消对基本算法的保守性。由于我们必须为取消其他节点进行投票,所以我们可能会忽略一个被取消的节点是在前面还是后面。这是由取消后的继任者来处理的,让他们能够在新的前任上稳定下来,除非我们能够确定一个没有被取消的前任,他将承担这个责任。

CLH queues need a dummy header node to get started. But we don't create them on construction, because it would be wasted effort if there is never contention. Instead, the node is constructed and head and tail pointers are set upon first contention. 

CLH队列需要一个虚拟头节点来启动。但我们不会在初始化时创造它们,因为如果没有竞争,就会白费力气。相反,节点被构造,头和尾指针被设置在第一个争用上。

Threads waiting on Conditions use the same nodes, but use an additional link. Conditions only need to link nodes in simple (non-concurrent) linked queues because they are only accessed when exclusively held. Upon await, a node is inserted into a condition queue. Upon signal, the node is transferred to the main queue. A special value of status field is used to mark which queue a node is on. 

等待条件的线程使用相同的节点,但是使用附加的链接。条件只需要在简单的(非并发的)连接队列中链接节点,因为只有在独占的情况下才访问它们。在等待的时候,一个节点被插入到一个条件队列中。在信号上,节点被转移到主队列。状态字段的一个特殊值用来标记一个节点所在的队列。


  • node结构:
static final class Node {
    //模式:共享||独占
    //共享模式
    static final Node SHARED = new Node();
    //独占模式
    static final Node EXCLUSIVE = null;

    //节点状态:
    static final int CANCELLED =  1; //当前线程被取消
    static final int SIGNAL    = -1; //当前节点的后继节点要运行,也就是unpark
    static final int CONDITION = -2; //当前节点在condition队列中等待
    static final int PROPAGATE = -3; '//后继的acquireShared能够得以执行,读写锁和信号量使用'

    volatile int waitStatus; //节点状态

    volatile Node prev; //前驱节点

    volatile Node next; //后继节点

    volatile Thread thread; //节点对应的线程

    Node nextWaiter;    //下一个等待者'waitStatus':节点状态

 '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. 
 
 'SIGNAL':该节点的后续版本(或将很快)被阻塞(通过park),因此当前节点在释放或取消时必须取消其后续版本。为了避免竞争,获取方法必须首先表明它们需要一个信号,然后重试原子获取,然后,在失败时,阻塞
 
 '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.
 
 'CANCELLED':由于超时或中断,该节点被取消。节点永远不会离开这个状态。特别地,一个被取消节点的线程再也不会阻塞
 
 '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.) 
 
 'CONDITION': 该节点当前处于一个条件队列中。在传输之前,它不会被用作同步队列节点,在此期间,状态将被设置为0。(这个值的使用与该字段的其他用途没有任何关系,但简化了力学。)
 
 '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.
 
 'PROPAGATE' :应该将一个释放的节点传播到其他节点。这是在doReleaseShared中设置的(仅用于头部节点),以确保传播继续,即使其他操作已经进行了干预
 
 0: None of the above The values are arranged numerically to simplify use. 
 '0' :以上这些值都没有按数字进行排列,以简化使用
 
 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).
 
 该字段被初始化为0,用于正常的同步节点,以及条件节点的条件。它使用CAS进行修改(或者在可能的情况下,无条件的volatile写操作)

  • AbstractQueuedSynchronizer 属性
public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
    /**
     * 头节点
     */
    private transient volatile Node head;

    /**
     * 尾节点
     */
    private transient volatile Node tail;

    /**
     * 同步器状态
     */
    private volatile int state;

    // Unsafe类实例
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long stateOffset; //state内存偏移地址
    private static final long headOffset;   //head内存偏移地址
    private static final long tailOffset;   //tail内存偏移地址
    private static final long waitStatusOffset; //waitStatus内存偏移地址
    private static final long nextOffset;   //next内存地址

    static {
        try {
            stateOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
            headOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("head"));
            tailOffset = unsafe.objectFieldOffset
                (AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
            waitStatusOffset = unsafe.objectFieldOffset
                (Node.class.getDeclaredField("waitStatus"));
            nextOffset = unsafe.objectFieldOffset
                (Node.class.getDeclaredField("next"));

        } catch (Exception ex) { throw new Error(ex); }
    }
}    
3.工作原理
  • 要点:

    • 锁.headNode用来在队列中占位,代表锁拥有者的node
    • 线程的休眠和唤醒都是通过 pred.waitStatus/-1 来判断
      • pred.waitStatus/-1 :当前线程lock(),自己休眠
      • pred.waitStatus/-1 :锁占用线程.unlock(),唤醒nextNode线程
    • 这样的好处是,锁的获取都是排队执行,减少了锁竞争。
  • 流程图

    • lock的时候:lock的时候,除了占用锁资源的线程。其它线程都会添加到synqueue中,并设置predNode的/waitstate=-1(用例判断当前线程是否休眠)/,然后资金park(直接休眠)

输入图片说明

* **unlock的时候**

输入图片说明 1. t1.unlock(), * 设置锁资源的state=0, * 检测headNode的waiteState/-1,然后/唤醒下一个线程/ * 设置headNode的/waiteState=0/ 2. t2被唤醒,继续lock() * 获取锁资源,设置锁ower=t2 * 设置/锁.head=t2Node/,将之前的headNode从队列中剔除 * 设置/t2Node.thread=null,t2Node.pred=null/,留空t2Node,作为占位

  • ReentrantLock.lock() 调用
    • 非公平锁走上面流程
    • 公平锁会先去判断队列是否为null
1.默认创建NonfairSync(非公平锁)

        final void lock() {
            if (compareAndSetState(0, 1))   '直接参与锁竞争'
                setExclusiveOwnerThread(Thread.currentThread()); //成功则设置锁的拥有者为当前线程
            else
                acquire(1);     //失败则调用 AbstractQueuedSynchronizer.acquire获取锁
        }





1.2 公平锁 fairSync 
        final void lock() {
            acquire(1); "AbstractQueuedSynchronizer.acquire "
        }
        
        "公平锁的tryAcquire"
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() && 
                    compareAndSetState(0, acquires)) {
                    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:判断node队列的头尾是否相同,节点的下一个节点是否为null"
        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. AbstractQueuedSynchronizer.acquire 

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
    
    2.1 tryAcquire:调用ReentrantLock.NonfairSync.tryAcquire(1),最后调用Sync.nonfairTryAcquire
    2.2 addWaiter(Node.EXCLUSIVE):添加当前线程到队列中
    2.3 acquireQueued(final Node node, int arg):循环获取锁资源(cas)
    2.4 selfInterrupt():自己中断。
    
'2.1  tryAcquire(1)获取锁资源:调用ReentrantLock.NonfairSync.tryAcquire(1)'

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires); //调用Sync.nonfairTryAcquire
    }

    '2.1.1 调用Sync.nonfairTryAcquire(1)'
    "非公平锁的tryAcquire"
    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState(); //当前lock资源状态
        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;
    }    

'2.2 addWaiter(Node.EXCLUSIVE) :添加队列'

    private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode); //创建node
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;   //获取锁队列的尾节点
        if (pred != null) { //尾节点不为null
            node.prev = pred;   //将node节点的前驱设置为尾节点
            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;
                }
            }
        }
    }    

'2.3 acquireQueued(final Node node, int arg):循环获取锁资源(cas)'    

    ① 判断结点的前驱是否为head并且当前节点是否成功获取(资源)。

  ② 若步骤①均满足,则设置结点为head,之后会判断是否finally模块,然后返回。

  ③ 若步骤①不满足,则判断是否需要park当前线程,是否需要park当前线程的逻辑是判断结点的前驱结点的状态是否为SIGNAL,若是,则park当前结点,否则,不进行park操作。

  ④ 若park了当前线程,之后某个线程对本线程unpark后,并且本线程也获得机会运行。那么,将会继续进行步骤①的判断。

    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;  //默认当前节点获取失败
        try {
            boolean interrupted = false;
            for (;;) {  //无限循环
                final Node p = node.predecessor();  //获取node节点的前驱节点
                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) //当前驱状态为 SIGNAL
     
            // 可以进行park操作
            return true;
        
        if (ws > 0) {
            表示状态为CANCELLED,为1
            do {
                node.prev = pred = pred.prev; // 找到pred结点前面最近的一个状态不为CANCELLED的结点
            } while (pred.waitStatus > 0);
            pred.next = node; // 赋值pred结点的next域   
        } else { 
            // 为PROPAGATE -3 或者是0 表示无状态,(为CONDITION -2时,表示此节点在condition queue中) 
            // 比较并设置前驱结点的状态为SIGNAL
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        // 不能进行park操作
        return false;
    }
    
    '进行park操作并且返回该线程是否被中断'
    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);  // 在许可可用之前禁用当前线程,并且设置了blocker
        return Thread.interrupted();  // 当前线程是否已被中断,并清除中断标记位
    }
    
    '获取失败,取消继续获取(资源)'
    private void cancelAcquire(Node node) {
        // Ignore if node doesn't exist
        //节点为null,返回
        if (node / null)
            return;
        //设置node的线程为null
        node.thread = null;

        // 获取前期,并找到前驱中状态不为 CANCELLED 的节点
        Node pred = node.prev;
        while (pred.waitStatus > 0)
            node.prev = pred = pred.prev;

        Node predNext = pred.next;

        // 设置节点的状态为CANCELLED
        node.waitStatus = Node.CANCELLED;

        // 如果节点是尾节点,设置尾节点是前驱
        if (node / tail && compareAndSetTail(node, pred)) {
            compareAndSetNext(pred, predNext, null); //前期下一个节点为null
        } else { //当前节点不是尾节点
            // If successor needs signal, try to set pred's next-link
            // so it will get one. Otherwise wake it up to propagate.
            int ws;
            if (pred != head &&
                ((ws = pred.waitStatus) / Node.SIGNAL ||
                 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
                pred.thread != null) {
                // (pred结点不为头结点,并且pred结点的状态为SIGNAL)或者 
                // pred结点状态小于等于0,并且比较并设置等待状态为SIGNAL成功,并且pred结点所封装的线程不为空
                Node next = node.next;
                if (next != null && next.waitStatus <= 0)
                    compareAndSetNext(pred, predNext, next);
            } else {
                unparkSuccessor(node);  '释放当前节点'
            }

            node.next = node; // help GC
        }
    }    
    'unparkSuccessor:释放后继结点'
    private void unparkSuccessor(Node node) {

        // 获取node结点的等待状态
        int ws = node.waitStatus;
        if (ws < 0) 
            // 状态值小于0,为SIGNAL -1 或 CONDITION -2 或 PROPAGATE -3
            // 比较并且设置结点等待状态,设置为0
            compareAndSetWaitStatus(node, ws, 0);


        // 获取node节点的下一个结点
        Node s = node.next;
        if (s / null || s.waitStatus > 0) { 
            // 下一个结点为空或者下一个节点的等待状态大于0,即为CANCELLED
            // s赋值为空
            s = null; 
            // 从尾结点开始从后往前开始遍历
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0) 
                    // 找到等待状态小于等于0的结点,找到最前的状态小于等于0的结点
                    // 保存结点
                    s = t;
        }
        if (s != null) // 该结点不为为空,释放许可
            LockSupport.unpark(s.thread);'唤醒下一个节点'
    }    
  • ReentrantLock.lock() 调用

'调用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;
    }
    
'减少锁的持有次数'
    protected final boolean tryRelease(int releases) {
        int c = getState() - releases; //获取释放的次数
        if (Thread.currentThread() != getExclusiveOwnerThread())    //如果当前节点没有持有锁资源则抛出异常
            throw new IllegalMonitorStateException();
        boolean free = false;
        if (c / 0) {   //锁资源为0
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值