ReentrantLock source code, CAS, AQS, CountDownLatch

CASE

Compare And Swap (Compare And Exchange) / Spin / Spin lock / No lock (no weight lock)

Because it often cooperates with loop operations until it is completed, it refers to a type of operation

cas (v, a, b), variable v, expected value a, modified value b

ABA problem, your girlfriend experienced someone else during the time you left, spin is you waiting idly, wait until she accepts you

Solution (version number AtomicStampedReference), the basic type simple value does not require a version number

Typical applications of CAS
are the classes under java.util.concurrent.atomic package, which are mostly implemented using CAS operations (eg. AtomicInteger.java, AtomicBoolean, AtomicLong). The following is a partial implementation of AtomicInteger.java to explain the implementation of these atomic classes.
 

AQS:

AQS (AbstractQueuedSynchronizer), AQS is a set of synchronization frameworks provided under JDK for implementing blocking locks and related synchronizers based on FIFO wait queues. This abstract class is designed to serve as a base class for synchronizers that can use atomic int values ​​to represent state. If you have seen a source implementation similar to the CountDownLatch class, you will find that there is an internal class Sync that inherits AbstractQueuedSynchronizer. It can be seen that CountDownLatch is a synchronizer based on the AQS framework. There are many similar synchronizers under JUC. (eg. Semaphore)
Insert picture description here

AQS principle overview

The core idea of ​​AQS is that, if the requested shared resource is idle, the thread currently requesting the resource is set as a valid worker thread, and the shared resource is set in the locked state. If the requested shared resource is occupied, then a mechanism for thread blocking waiting and lock allocation when awakened is required. This mechanism AQS is implemented with a CLH queue lock, that is, a thread that cannot temporarily obtain a lock is added to the queue.

The CLH (Craig, Landin, and Hagersten) queue is a virtual two-way queue (the virtual two-way queue does not have a queue instance, only the association between nodes). AQS encapsulates each thread requesting shared resources into a CLH lock queue to implement lock allocation.

AQS (AbstractQueuedSynchronizer) schematic diagram:
Insert picture description here

AQS uses an int member variable to indicate the synchronization status , and completes the queuing work of the resource acquisition thread through the built-in FIFO queue. AQS uses CAS to perform atomic operations on the synchronization state to modify its value .

    /**
     * The synchronization state.
     *//共享变量,使用volatile修饰保证线程可见性
     */
    private volatile int state;

    /**
     * Returns the current value of synchronization state.
     * This operation has memory semantics of a {@code volatile} read.
     * @return current state value
     */
    protected final int getState() {
        return state;
    }

    /**
     * Sets the value of synchronization state.
     * This operation has memory semantics of a {@code volatile} write.
     * @param newState the new state value
     */
    protected final void setState(int newState) {
        state = newState;
    }

    
    /**
     * Atomically sets synchronization state to the given updated
     * value if the current state value equals the expected value.
     * This operation has memory semantics of a {@code volatile} read
     * and write.
     *
     * @param expect the expected value
     * @param update the new value
     * @return {@code true} if successful. False return indicates that the actual
     *         value was not equal to the expected value.
     * 使用CAS底层底层实现进行修改,底层为本地native方法,是c++书写的,
     * 底层c++ LOCK_IF_MP(mp)  指令:lock cmpxchg 指令 
     * lock指令在执行后面指令的时候锁定一个北桥信号,实现原子性
     *  多线程情况下,与CPU交互的底层指令会加lock锁,
    
     */
    protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

 

AQS Shares Resources
AQS defines two resource sharing methods:

Exclusive: Only one thread can execute, such as ReentrantLock. It can be divided into fair locks and unfair locks:
fair locks: in accordance with the queuing order of threads in the queue, the first one gets the lock first // lock (); method
unfair locks: when the thread wants to acquire the lock, ignore the queue Order directly to grab the lock, whoever grabs it is who's // tryLock () method
Share: multiple threads can execute at the same time, such as Semaphore / CountDownLatch. Semaphore, CountDownLatch, CyclicBarrier, ReadWriteLock.
ReentrantReadWriteLock can be seen as a combination, because ReentrantReadWriteLock is a read-write lock that allows multiple threads to read a resource at the same time.

The bottom layer of AQS uses the template method pattern
. The design of the synchronizer is based on the template method pattern. If you need to customize the synchronizer, the general way is as follows:

The user inherits AbstractQueuedSynchronizer and overrides the specified method. (These rewriting methods are very simple, nothing more than the acquisition and release of the shared resource state.)
Combine AQS in the implementation of custom synchronization components and call their template methods, and these template methods will call the methods rewritten by the user.

 

AQS uses the template method mode. When customizing the synchronizer, you need to rewrite the following template methods provided by AQS:

isHeldExclusively()//该线程是否正在独占资源。只有用到condition才需要去实现它。
tryAcquire(int)//独占方式。尝试获取资源,成功则返回true,失败则返回false。
tryRelease(int)//独占方式。尝试释放资源,成功则返回true,失败则返回false。
tryAcquireShared(int)//共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
tryReleaseShared(int)//共享方式。尝试释放资源,成功则返回true,失败则返回false。

 

By default, each method throws UnsupportedOperationException. The implementation of these methods must be internally thread safe, and should generally be short rather than blocking. The other methods in the AQS class are final, so they cannot be used by other classes. Only these methods can be used by other classes.

Taking ReentrantLock as an example, the state is initialized to 0, indicating an unlocked state. When A thread lock (), it will call tryAcquire () to monopolize the lock and state + 1. After that, other threads will fail when they tryAcquire (). Until A thread unlock () to state = 0 (that is, release the lock), other threads will have the opportunity to acquire the lock. Of course, before releasing the lock, the A thread itself can repeatedly acquire the lock (state will accumulate), which is the concept of reentrancy. But pay attention to how many times you have to release it, so as to ensure that the state can return to zero state.

Taking CountDownLatch as an example, the task is divided into N sub-threads to execute, and the state is also initialized to N (note that N must be consistent with the number of threads). The N sub-threads are executed in parallel. After each sub-thread is executed, countDown () is performed once, and the state will be reduced by 1 CAS (Compare and Swap). After all the sub-threads are executed (state = 0), the main calling thread will be unpark (), and then the main calling thread will return from the await () function to continue the remaining actions.

Generally speaking, custom synchronizers are either exclusive methods or sharing methods, and they only need to implement one of tryAcquire-tryRelease and tryAcquireShared-tryReleaseShared. But AQS also supports custom synchronizer to achieve both exclusive and shared methods, such as ReentrantReadWriteLock.

AQS component summary
Semaphore (semaphore): allows multiple threads to access at the same time, both synchronized and ReentrantLock only allow one thread to access a resource at a time, Semaphore (semaphore) can specify multiple threads to access a resource at the same time.
CountDownLatch (Countdown Timer): CountDownLatch is a synchronization tool class used to coordinate the synchronization between multiple threads. This tool is usually used to control thread waiting. It can make a thread wait until the countdown ends before it starts execution.
CyclicBarrier (Cyclic Barrier): CyclicBarrier is very similar to CountDownLatch, it can also achieve technical waiting between threads, but its function is more complicated and powerful than CountDownLatch. The main application scenario is similar to CountDownLatch. CyclicBarrier literally means Cyclic Barrier. What it needs to do is to allow a group of threads to block when they reach a barrier (also called a synchronization point). The barrier will not open until the last thread reaches the barrier, and all threads that are blocked by the barrier will continue to work. The default construction method of CyclicBarrier is CyclicBarrier (int parties), and its parameter indicates the number of threads intercepted by the barrier. Each thread calls the await () method to tell CyclicBarrier that I have reached the barrier, and then the current thread is blocked.
 

public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
    /** Synchronizer providing all implementation mechanics */
//tryLock()和try()都是继承内部类Sync,
    private final Sync sync;


    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        /**
         * Performs {@link Lock#lock}. The main reason for subclassing
         * is to allow fast path for nonfair version.
         */
        abstract void lock();

        /**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         */
        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;
        }

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

        protected final boolean isHeldExclusively() {
            // While we must in general read state before owner,
            // we don't need to do so to check if current thread is owner
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

        final ConditionObject newCondition() {
            return new ConditionObject();
        }

        // Methods relayed from outer class

        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }

        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }

        final boolean isLocked() {
            return getState() != 0;
        }

        /**
         * Reconstitutes the instance from a stream (that is, deserializes it).
         */
        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }

而Sync实现AbstractQueuedSynchronizer类

    ReentrantLock如何处理线程中断的?
    AbstractQueuedSynchronizer的parkAndCheckInterrupt()
    阻塞队列使用的LockSupport.park();

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


因为LockSupport.park(),无法响应Thread.interrupt(); 所以当unpark()后使用Thread.interrupted()来判断线程是否有中断过。如果中断过整个唤醒的线程在外层方法会继续执行一次中断,详情源码如下:

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


The difference between tryLock and lock

1. TryLock will return directly regardless of whether it is received or not; if the lock cannot be obtained, it will wait forever.

2. tryLock can be interrupted.

Lock tryLock ();

 

    ReentrantLock reentrantLock = new ReentrantLock();
        reentrantLock.tryLock();
    
    public boolean tryLock() {
        //调用SYNC的非公平锁方法,去尝试获得锁
        return sync.nonfairTryAcquire(1);
    }


         /**
         * Performs non-fair tryLock.  tryAcquire is implemented in
         * subclasses, but both need nonfair try for trylock method.
         */
        final boolean nonfairTryAcquire(int acquires) {
            //获取当前线程
            final Thread current = Thread.currentThread();
            int c = getState();
//如果当前state为0则表示未被加锁
            if (c == 0) {
//调用CAS方法,将state的状态从未加锁0改为加锁状态1,多线程情况下,可能会出现加锁失败
                if (compareAndSetState(0, acquires)) {  
//加锁成功,则把当前线程设置为,当前独占锁的拥有者
                    setExclusiveOwnerThread(current);
//并返回
                    return true;
                }
            }
//如果当前锁的拥有者是当前线程
            else if (current == getExclusiveOwnerThread()) {
//则将state加1,表示再次加锁,释放锁时候,只有当state值为0,才表示释放锁完成
                int nextc = c + acquires;
//当nextc小于0,则表示超过了,锁的最大计数器
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
//设置state为nextc,并返回true
                setState(nextc);
                return true;
            }
//否则返回false
            return false;
        }

 lock () Unfair lock: when it comes, it will seize first, but not get it, then enter the queue

    //默认非公平锁,true表示公平锁,false表示非公平锁,    
    ReentrantLock reentrantLock = new ReentrantLock(false);
        reentrantLock.lock();

    //NonfairSync extends Sync 使用NonfairSync中的lock
        public void lock() {
            sync.lock();
        }
        
        final void lock() {
    //采用cas设置state为1
            if (compareAndSetState(0, 1))
   //设置成功,则表示获得锁,并将当前线程设置为锁的拥有者
                setExclusiveOwnerThread(Thread.currentThread());
            else
    //否则
                acquire(1);
        }
    
    public final void acquire(int arg) {
//// 如果获取不到锁,就放进等待队列(addWaiter),然后阻塞直到成功获取到锁
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }


  /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
//获取当前线程
            final Thread current = Thread.currentThread();
            int c = getState();
//如果标志State为0
            if (c == 0) {
//当前等待队列为空,并进行CAS操作成功
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
//则将当前线程设置为独占锁持有者
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //如果当前锁的拥有者是当前线程
            else if (current == getExclusiveOwnerThread()) {
//则将state加1,表示再次加锁,释放锁时候,只有当state值为0,才表示释放锁完成
                int nextc = c + acquires;
//当nextc小于0,则表示超过了,锁的最大计数器
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
//设置state为nextc,并返回true
                setState(nextc);
                return true;
            }
//否则返回false
            return false;
        
        }
    }


    // 从clh中选一个线程获取占用资源
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                // 当节点的先驱是head的时候,就可以尝试获取占用资源了tryAcquire
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    // 如果获取到资源,则将当前节点设置为头节点head
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                // 如果获取失败的话,判断是否可以休息,可以的话就进入waiting状态,直到被unpark()
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(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) { 
                // tail为null,说明还没初始化,此时需进行初始化工作
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                // 否则的话,将当前线程节点作为tail节点加入到CLH中去
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

lock () fair locks  fair locks: old threads are queued, and new threads are also queued, not preempted

 

  ReentrantLock reentrantLock = new ReentrantLock(true);
        reentrantLock.lock();

    public void lock() {
        sync.lock();
    }

    //FairSync extends Sync,使用FairSync中的lock()方法
        final void lock() {
            acquire(1);
        }

    public final void acquire(int arg) {
//// 如果获取不到锁,就放进等待队列(addWaiter),然后阻塞直到成功获取到锁
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }


  /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
//获取当前线程
            final Thread current = Thread.currentThread();
            int c = getState();
//如果标志State为0
            if (c == 0) {
//当前等待队列为空,并进行CAS操作成功
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
//则将当前线程设置为独占锁持有者
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //如果当前锁的拥有者是当前线程
            else if (current == getExclusiveOwnerThread()) {
//则将state加1,表示再次加锁,释放锁时候,只有当state值为0,才表示释放锁完成
                int nextc = c + acquires;
//当nextc小于0,则表示超过了,锁的最大计数器
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
//设置state为nextc,并返回true
                setState(nextc);
                return true;
            }
//否则返回false
            return false;
        
        }
    }


    // 从clh中选一个线程获取占用资源
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                // 当节点的先驱是head的时候,就可以尝试获取占用资源了tryAcquire
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    // 如果获取到资源,则将当前节点设置为头节点head
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                // 如果获取失败的话,判断是否可以休息,可以的话就进入waiting状态,直到被unpark()
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(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) { 
                // tail为null,说明还没初始化,此时需进行初始化工作
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                // 否则的话,将当前线程节点作为tail节点加入到CLH中去
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

 CountDownLatch class

 

public class CountDownLatch {
    /**
     * 基于AQS的内部Sync
     * 使用AQS的state来表示计数count.
     */
    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;

        Sync(int count) {
            // 使用AQS的getState()方法设置状态
            setState(count);
        }

        int getCount() {
            // 使用AQS的getState()方法获取状态
            return getState();
        }

        // 覆盖在共享模式下尝试获取锁
        protected int tryAcquireShared(int acquires) {
            // 这里用状态state是否为0来表示是否成功,为0的时候可以获取到返回1,否则不可以返回-1
            return (getState() == 0) ? 1 : -1;
        }

        // 覆盖在共享模式下尝试释放锁
        protected boolean tryReleaseShared(int releases) {
            // 在for循环中Decrement count直至成功;
            // 当状态值即count为0的时候,返回false表示 signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
    }

    private final Sync sync;

    // 使用给定计数值构造CountDownLatch
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

    // 让当前线程阻塞直到计数count变为0,或者线程被中断
    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

    // 阻塞当前线程,除非count变为0或者等待了timeout的时间。当count变为0时,返回true
    public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }

    // count递减
    public void countDown() {
        sync.releaseShared(1);
    }

    // 获取当前count值
    public long getCount() {
        return sync.getCount();
    }

    public String toString() {
        return super.toString() + "[Count = " + sync.getCount() + "]";
    }
}

 

 

Published 11 original articles · Likes2 · Visits 140

Guess you like

Origin blog.csdn.net/sinat_38195280/article/details/105346499