J.U.C Analysis and Interpretation 2 (Origin of AQS)

Keywords: Java JDK Windows less

Preface

Previously, you showed how JDK implements exclusive and shared locks by implementing custom ReentrantLock and custom ReentrantReadWriteLock.

So how do ReentrantLock and ReentrantReadWritreLock in the actual JDK source work?Can our existing custom code go any further?

The answer is yes.Note that if you look at my two previous Lock friends, you should.The exclusive lock section of a custom ReentrantReadWriteLock is essentially the same as a custom ReentrantLock.

That is, different Lock s have similar implementations.So can we extract the public parts and write more elegantly?

This blog is about extracting common code, introducing template design patterns, and writing a custom AQS using some Java features.

Of course, the difference between AQS implementation in source code and our custom AQS will be analyzed, and some advanced applications in source AQS will be interpreted, such as the number of read and write locks held by AQS through a state (an int value CAS operation actually solves the exclusive number of custom read and write locks held).

If you've seen friends in the source code, you'll find that ReentrantLock in the source code customizes a Sync that inherits an AbstratQueueSynchronizer (AQS for short).Then methods such as ReentrantLock's tryLock in the source code call the corresponding subclass of Sync (FairSync or NonFairSync, that is, whether it is a fair lock) to implement the corresponding function.Furthermore, only tryAcquire and lock methods are implemented by ReentrantLock, and the other methods are provided by AQS.Lock is implemented by FairSync and NonFairSync, respectively.While tryAcquire is implemented by FairSync and NonFairSync's parent Sync, NonFairSync's tryLock directly calls the parent Sync's nonfairTryAcquire method.

ReentrantReadWriteLock adds ReadLock and WriteLock, which are implemented by calling different methods of Sync.

Some small partners will find such a complex relationship, clearly a lock is more complex, but also so abstract.Extracting an AQS is abstract enough, and each lock also has an entire Sync, FairSync, NonFairSync internal class, which is ReadLock, WriteLock, as appropriate.The goal is to encapsulate code and improve code reuse.Of course, when you look at the actual source code, you will find it very comfortable.More in line with the design concept (think about the project you received, there are thousands of lines of code in a class that you can't modify at all).

About reading the source code, just tell me how I feel.The core is insistence, the most important is the global view, and the most important is accumulation.

I've been reading the source code (not just Java) for almost two years.From the earliest Windows kernel source, to the back front-end framework source, to this year's Java source reading.The earliest Windows kernel source, it was really dauntless and a very painful experience.At that time, a day might be twenty pages long, depending on your luck.But that period of time has brought me a lot, including what system memory management, memory user and kernel state, and system context accumulation, which brings a lot of improvement for me later.And the reading of the front-end source code at the back also gives me some ideas to start contacting the source code.Finally, this year's Java source, with the translation (involving language design) padding of golang's foreign language blogs last year, it gradually became a little bit of a sense of reading the source (I feel a little on the road).Therefore, the core is persistence.

As for the global view, on the one hand, many times there are too many sources and we often get lost. We need to grasp the main line and our own purpose.It would be better if you could have an XMIND or a better blog to guide you.For example, this AQS split, I learned from James, the big man of NetEasy Cloud.Although I had accumulated JUC learning before, the big man's AQS split really made me have a deeper understanding of AQS.On the other hand, you need to grasp the depth you should study (a little deeper at your own level of competence), rather than just grab every word of the source code.At the beginning of this year, I wanted to study the IOC source, according to a big guy Article For two or three weeks.But one after another they forget.Although this experience still has some accumulated value for me (which is good value for my recent study of the Spring Application run process), the cost-effectiveness ratio is too low if you exceed your capabilities too much.

Finally, accumulation, which I attach great importance to.Since my senior year, I have a deeper understanding of the word accumulation.Many times, we read some books and study some principles, although later I feel forgotten, but the accumulation still exists.Just like when I was learning programming, I often felt the accumulation of computer network, computer principles, distributed topics and other experiences in college.Now many people are too concerned about instant value (that is, I learned it right away and it works right away), and I believe that the climb of technology can not be separated from years of accumulation.

If you are interested in reading the source code, you can tell me.Consider writing an article to briefly talk about source reading.

One, Simple JUC (Version 1):

Here is a brief review of the simplified ReentrantLock and ReentrantReadWriteLock implementations that were previously implemented.

1.JarryReentrantLock:

    package tech.jarry.learning.netease.locks2;
    
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.concurrent.atomic.AtomicReference;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.LockSupport;
    
    /**
     * @Description:  Imitate ReentrantLock to achieve its basic functions and features
     * @Author: jarry
     */
    public class JarryReentrantLock {
    
        private AtomicInteger count = new AtomicInteger(0);
        private AtomicReference<Thread> owner = new AtomicReference<>();
        private LinkedBlockingQueue<Thread> waiters = new LinkedBlockingQueue<>();
    
        public void lock() {
            int arg = 1;
            if (!tryLock(arg)){
                waiters.offer(Thread.currentThread());
                while (true){
                    Thread head = waiters.peek();
                    if (head == Thread.currentThread()){
                        if (!tryLock(arg)){
                            LockSupport.park();
                        } else {
                            waiters.poll();
                            return;
                        }
                    } else {
                        LockSupport.park();
                    }
                }
            }
        }
    
        public void unlock() {
            int arg = 1;
            if (tryUnlock(arg)){
                Thread head = waiters.peek();
                if (head != null){
                    LockSupport.unpark(head);
                }
            }
        }
    
        public boolean tryLock(int acquires) {
            int countValue = count.get();
            if (countValue != 0){
                if (Thread.currentThread() == owner.get()){
                    count.set(countValue+acquires);
                    return true;
                }else{
                    return false;
                }
            }else {
                if (count.compareAndSet(countValue,countValue+acquires)){
                    owner.set(Thread.currentThread());
                    return true;
                } else {
                    return false;
                }
            }
        }
    
        private boolean tryUnlock(int releases) {
            if (Thread.currentThread() != owner.get()){
                throw new IllegalMonitorStateException();
            } else {
                int countValue = count.get();
                int countNextValue = countValue - releases;
                count.compareAndSet(countValue,countNextValue);
                if (countNextValue == 0){
                    owner.compareAndSet(Thread.currentThread(),null);
                    return true;
                } else {
                    return false;
                }
            }
        }
    
        public void lockInterruptibly() throws InterruptedException {
    
        }
    
        public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
            return false;
        }
    
        public Condition newCondition() {
            return null;
        }
    }

2.JarryReadWriteLock:

    package tech.jarry.learning.netease.locks2;
    
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.concurrent.atomic.AtomicReference;
    import java.util.concurrent.locks.LockSupport;
    
    /**
     * @Description: 
     * @Author: jarry
     */
    public class JarryReadWriteLock {
    
        volatile AtomicInteger readCount = new AtomicInteger(0);
        AtomicInteger writeCount = new AtomicInteger(0);
        AtomicReference<Thread> owner = new AtomicReference<>();
        public volatile LinkedBlockingQueue<WaitNode> waiters = new LinkedBlockingQueue<>();
    
        class WaitNode{
            Thread thread = null;
            // Indicates the type of lock you want to strive for.0 for write lock (exclusive lock) and 1 for read lock (shared lock)
            int type = 0;
            int arg = 0;
    
            public WaitNode(Thread thread, int type, int arg) {
                this.type = type;
                this.thread = thread;
                this.arg = arg;
            }
        }
    
        /**
         * Acquire exclusive locks (for exclusive locks)
         */
        public void lock(){
            int arg = 1;
            if (!tryLock(arg)){
                WaitNode waitNode = new WaitNode(Thread.currentThread(), 0, arg);
                waiters.offer(waitNode);
    
                while (true){
                    WaitNode headNote = waiters.peek();
                    if (headNote !=null && headNote.thread == Thread.currentThread()){
                        if (!tryLock(headNote.arg)){
                            LockSupport.park();
                        } else {
                            waiters.poll();
                            return;
                        }
                    }else {
                        LockSupport.park();
                    }
                }
            }
        }
    
        /**
         * Unlock (for exclusive locks)
         */
        public void unlock(){
            int arg = 1;
            if (tryUnlock(arg)){
                WaitNode head = waiters.peek();
                if (head == null){
                    return;
                }
                LockSupport.unpark(head.thread);
            }
        }
    
        /**
         * Attempt to acquire an exclusive lock (for exclusive locks)
         * @param acquires Number of times to lock.Typically, waitNode.arg is passed in (1 in this code).Why don't you use a constant 1 and you don't know?
         * @return
         */
        public boolean tryLock(int acquires){
            if (readCount.get() == 0){
                int writeCountValue = writeCount.get();
                if (writeCountValue == 0){
                    if (writeCount.compareAndSet(writeCountValue,writeCountValue+acquires)){
                        owner.set(Thread.currentThread());
                        return true;
                    }
                } else {
                    if (Thread.currentThread() == owner.get()){
                        writeCount.set(writeCountValue+acquires);
                        return true;
                    }
                }
            }
            return false;
        }
    
        /**
         * Attempt to unlock (for exclusive locks)
         * @param releases Used to set the number of unlocks.General incoming waitNode.arg
         * @return
         */
        public boolean tryUnlock(int releases){
            if (owner.get() != Thread.currentThread()){
                throw new IllegalMonitorStateException();
            }
            int writeCountValue = writeCount.get();
            writeCount.set(writeCountValue-releases);
            if (writeCount.get() == 0){
                owner.compareAndSet(Thread.currentThread(),null);
                return true;
            } else {
                return false;
            }
        }
    
        /**
         * Acquire shared locks (for shared locks)
         */
        public void lockShared(){
            int arg = 1;
            if (!tryLockShared(arg)){
                WaitNode waitNode = new WaitNode(Thread.currentThread(),1,arg);
                waiters.offer(waitNode);
    
                while (true){
                    WaitNode head = waiters.peek();
                    if (head != null && head.thread == Thread.currentThread()){
                        if (tryLockShared(head.arg)){
                            waiters.poll();
    
                            WaitNode newHead = waiters.peek();
                            if (newHead != null && newHead.type == 1){
                                LockSupport.unpark(newHead.thread);
                            }
                            return;
                        } else {
                            LockSupport.park();
                        }
                    } else {
                        LockSupport.park();
                    }
                }
            }
        }
    
        /**
         * Unlock (for shared locks)
         */
        public boolean unLockShared(){
            int arg = 1;
            if (tryUnLockShared(arg)){
                WaitNode head = waiters.peek();
                if (head != null){
                    LockSupport.unpark(head.thread);
                }
                return true;
            }
            return false;
        }
    
        /**
         * Attempt to acquire shared locks (for shared locks)
         * @param acquires
         * @return
         */
        public boolean tryLockShared(int acquires){
            while (true){
                if (writeCount.get() == 0 || owner.get() == Thread.currentThread()){
                    int readCountValue = readCount.get();
                    if (readCount.compareAndSet(readCountValue, readCountValue+acquires)){
                        return true;
                    }
                }
                return false;
            }
        }
    
        /**
         * Attempt to unlock (for shared locks)
         * @param releases
         * @return
         */
        public boolean tryUnLockShared(int releases){
            while (true){
                int readCountValue = readCount.get();
                int readCountNext = readCountValue - releases;
                if (readCount.compareAndSet(readCountValue,readCountNext)){
                    return readCountNext == 0;
                }
            }
        }
    
    }

2. Simple JUC (Version 2):

Obviously, in the code above, methods such as JarryReentrantLock's tryLock are similar to those used in JarryReadWriteLock to share locks (originally copied from JarryReentrantLock).This requires the introduction of a template approach (see note Design Patterns - Template Approach for details).Extract public methods through a commonMask class.

1.CommonMask:

    package tech.jarry.learning.netease.locks3;
    
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.concurrent.atomic.AtomicReference;
    import java.util.concurrent.locks.LockSupport;
    
    /**
     * @Description: 
     * @Author: jarry
     */
    public class CommonMask {
    
        volatile AtomicInteger readCount = new AtomicInteger(0);
        AtomicInteger writeCount = new AtomicInteger(0);
        AtomicReference<Thread> owner = new AtomicReference<>();
        public volatile LinkedBlockingQueue<WaitNode> waiters = new LinkedBlockingQueue<>();
    
        class WaitNode{
            Thread thread = null;
            // Indicates the type of lock you want to strive for.0 for write lock (exclusive lock) and 1 for read lock (shared lock)
            int type = 0;
            int arg = 0;
    
            public WaitNode(Thread thread, int type, int arg) {
                this.type = type;
                this.thread = thread;
                this.arg = arg;
            }
        }
    
        /**
         * Acquire exclusive locks (for exclusive locks)
         */
        public void lock(){
            int arg = 1;
            if (!tryLock(arg)){
                WaitNode waitNode = new WaitNode(Thread.currentThread(), 0, arg);
                waiters.offer(waitNode);
    
                while (true){
                    WaitNode headNote = waiters.peek();
                    if (headNote !=null && headNote.thread == Thread.currentThread()){
                        if (!tryLock(headNote.arg)){
                            LockSupport.park();
                        } else {
                            waiters.poll();
                            return;
                        }
                    }else {
                        LockSupport.park();
                    }
                }
            }
        }
    
        /**
         * Unlock (for exclusive locks)
         */
        public void unlock(){
            int arg = 1;
            if (tryUnlock(arg)){
                WaitNode head = waiters.peek();
                if (head == null){
                    return;
                }
                LockSupport.unpark(head.thread);
            }
        }
    
        /**
         * Attempt to acquire an exclusive lock (for exclusive locks)
         * @param acquires Number of times to lock.Typically, waitNode.arg is passed in (1 in this code).Why don't you use a constant 1 and you don't know?
         * @return
         */
        public boolean tryLock(int acquires){
            if (readCount.get() == 0){
                int writeCountValue = writeCount.get();
                if (writeCountValue == 0){
                    if (writeCount.compareAndSet(writeCountValue,writeCountValue+acquires)){
                        owner.set(Thread.currentThread());
                        return true;
                    }
                } else {
                    if (Thread.currentThread() == owner.get()){
                        writeCount.set(writeCountValue+acquires);
                        return true;
                    }
                }
            }
            return false;
        }
    
        /**
         * Attempt to unlock (for exclusive locks)
         * @param releases Used to set the number of unlocks.General incoming waitNode.arg
         * @return
         */
        public boolean tryUnlock(int releases){
            if (owner.get() != Thread.currentThread()){
                throw new IllegalMonitorStateException();
            }
            int writeCountValue = writeCount.get();
            writeCount.set(writeCountValue-releases);
            if (writeCount.get() == 0){
                owner.compareAndSet(Thread.currentThread(),null);
                return true;
            } else {
                return false;
            }
        }
    
        /**
         * Acquire shared locks (for shared locks)
         */
        public void lockShared(){
            int arg = 1;
            if (!tryLockShared(arg)){
                WaitNode waitNode = new WaitNode(Thread.currentThread(),1,arg);
                waiters.offer(waitNode);
    
                while (true){
                    WaitNode head = waiters.peek();
                    if (head != null && head.thread == Thread.currentThread()){
                        if (tryLockShared(head.arg)){
                            waiters.poll();
    
                            WaitNode newHead = waiters.peek();
                            if (newHead != null && newHead.type == 1){
                                LockSupport.unpark(newHead.thread);
                            }
                            return;
                        } else {
                            LockSupport.park();
                        }
                    } else {
                        LockSupport.park();
                    }
                }
            }
        }
    
        /**
         * Unlock (for shared locks)
         */
        public boolean unLockShared(){
            int arg = 1;
            if (tryUnLockShared(arg)){
                WaitNode head = waiters.peek();
                if (head != null){
                    LockSupport.unpark(head.thread);
                }
                return true;
            }
            return false;
        }
    
        /**
         * Attempt to acquire shared locks (for shared locks)
         * @param acquires
         * @return
         */
        public boolean tryLockShared(int acquires){
            while (true){
                if (writeCount.get() == 0 || owner.get() == Thread.currentThread()){
                    int readCountValue = readCount.get();
                    if (readCount.compareAndSet(readCountValue, readCountValue+acquires)){
                        return true;
                    }
                }
                return false;
            }
        }
    
        /**
         * Attempt to unlock (for shared locks)
         * @param releases
         * @return
         */
        public boolean tryUnLockShared(int releases){
            while (true){
                int readCountValue = readCount.get();
                int readCountNext = readCountValue - releases;
                if (readCount.compareAndSet(readCountValue,readCountNext)){
                    return readCountNext == 0;
                }
            }
        }
    }

2.JarryReentrantLock:

    package tech.jarry.learning.netease.locks3;
    
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.concurrent.atomic.AtomicReference;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.LockSupport;
    
    /**
     * @Description:  Imitate ReentrantLock to achieve its basic functions and features
     * @Author: jarry
     */
    public class JarryReentrantLock {
    
        private CommonMask commonMask = new CommonMask();
    
        public void lock() {
            commonMask.lock();
        }
    
        public void unlock() {
            commonMask.unlock();
        }
    
        public boolean tryLock(int acquire) {
            return commonMask.tryLock(acquire);
        }
    
        private boolean tryUnlock(int release) {
            return commonMask.tryUnlock(release);
        }
    }

3.JarryReadWriteLock:

    package tech.jarry.learning.netease.locks3;
    
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.concurrent.atomic.AtomicReference;
    import java.util.concurrent.locks.LockSupport;
    
    /**
     * @Description: 
     * @Author: jarry
     */
    public class JarryReadWriteLock {
    
        private CommonMask commonMask = new CommonMask();
    
        /**
         * Acquire exclusive locks (for exclusive locks)
         */
        public void lock(){
            commonMask.lock();
        }
    
        /**
         * Unlock (for exclusive locks)
         */
        public void unlock(){
            commonMask.unlock();
        }
    
        /**
         * Attempt to acquire an exclusive lock (for exclusive locks)
         * @param acquires Number of times to lock.Typically, waitNode.arg is passed in (1 in this code).Why don't you use a constant 1 and you don't know?
         * @return
         */
        public boolean tryLock(int acquires){
            return commonMask.tryLock(acquires);
        }
    
        /**
         * Attempt to unlock (for exclusive locks)
         * @param releases Used to set the number of unlocks.General incoming waitNode.arg
         * @return
         */
        public boolean tryUnlock(int releases){
            return commonMask.tryUnlock(releases);
        }
    
        /**
         * Acquire shared locks (for shared locks)
         */
        public void lockShared(){
            commonMask.lockShared();
        }
    
        /**
         * Unlock (for shared locks)
         */
        public boolean unLockShared(){
            return commonMask.unLockShared();
        }
    
        /**
         * Attempt to acquire shared locks (for shared locks)
         * @param acquires
         * @return
         */
        public boolean tryLockShared(int acquires){
            return tryLockShared(acquires);
        }
    
        /**
         * Attempt to unlock (for shared locks)
         * @param releases
         * @return
         */
        public boolean tryUnLockShared(int releases){
            return commonMask.tryUnLockShared(releases);
        }
    
    }

When you get here, you can clearly see that the overall code volume has decreased (this is only two Lock s).But the question arises as to whether it would seem too rigid to put all the methods in the parent CommonMask and call the subclasses (which is, to put it directly, simply throw the code away from the parent).This indicates that public code extraction did not work well before.

Reorganize your thinking and see what JarryReentrantLock and JarryReadWriteLock have in common.Think about it and find that the lock, unlock and other operations of the two methods are identical, but the actual running logic methods tryLock, tryUnlock, tryLockShared, tryUnLockShared are the four methods (in the framework source code, the doxxx method is often used to represent the actual running logic method).So CommonMask should implement methods other than these four, which are handed over to subclasses to implement as needed (in CommonMask, these four methods throw corresponding exceptions directly).

Finally, ReentrantLock is a fair lock, divided by an unfair lock.With the above adjustments, Jarry ReentrantLock can now implement its own methods to exhibit characteristics (fair/unfair lock selection).

3. Simple JUC (Version 3):

1.CommonMask:

    package tech.jarry.learning.netease.locks4;
    
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.concurrent.atomic.AtomicReference;
    import java.util.concurrent.locks.LockSupport;
    
    /**
     * @Description: 
     * @Author: jarry
     */
    public class CommonMask {
    
        volatile AtomicInteger readCount = new AtomicInteger(0);
        AtomicInteger writeCount = new AtomicInteger(0);
        AtomicReference<Thread> owner = new AtomicReference<>();
        public volatile LinkedBlockingQueue<WaitNode> waiters = new LinkedBlockingQueue<>();
    
        class WaitNode{
            Thread thread = null;
            // Indicates the type of lock you want to strive for.0 for write lock (exclusive lock) and 1 for read lock (shared lock)
            int type = 0;
            int arg = 0;
    
            public WaitNode(Thread thread, int type, int arg) {
                this.type = type;
                this.thread = thread;
                this.arg = arg;
            }
        }
    
        /**
         * Acquire exclusive locks (for exclusive locks)
         */
        public void lock(){
            int arg = 1;
            if (!tryLock(arg)){
                WaitNode waitNode = new WaitNode(Thread.currentThread(), 0, arg);
                waiters.offer(waitNode);
    
                while (true){
                    WaitNode headNote = waiters.peek();
                    if (headNote !=null && headNote.thread == Thread.currentThread()){
                        if (!tryLock(headNote.arg)){
                            LockSupport.park();
                        } else {
                            waiters.poll();
                            return;
                        }
                    }else {
                        LockSupport.park();
                    }
                }
            }
        }
    
        /**
         * Unlock (for exclusive locks)
         */
        public void unlock(){
            int arg = 1;
            if (tryUnlock(arg)){
                WaitNode head = waiters.peek();
                if (head == null){
                    return;
                }
                LockSupport.unpark(head.thread);
            }
        }
    
    
        /**
         * Acquire shared locks (for shared locks)
         */
        public void lockShared(){
            int arg = 1;
            if (!tryLockShared(arg)){
                WaitNode waitNode = new WaitNode(Thread.currentThread(),1,arg);
                waiters.offer(waitNode);
    
                while (true){
                    WaitNode head = waiters.peek();
                    if (head != null && head.thread == Thread.currentThread()){
                        if (tryLockShared(head.arg)){
                            waiters.poll();
    
                            WaitNode newHead = waiters.peek();
                            if (newHead != null && newHead.type == 1){
                                LockSupport.unpark(newHead.thread);
                            }
                            return;
                        } else {
                            LockSupport.park();
                        }
                    } else {
                        LockSupport.park();
                    }
                }
            }
        }
    
        /**
         * Unlock (for shared locks)
         */
        public boolean unLockShared(){
            int arg = 1;
            if (tryUnLockShared(arg)){
                WaitNode head = waiters.peek();
                if (head != null){
                    LockSupport.unpark(head.thread);
                }
                return true;
            }
            return false;
        }
    
        /**
         * Attempt to acquire an exclusive lock (for exclusive locks)
         * @param acquires
         * @return
         */
        public boolean tryLock(int acquires){
            throw new UnsupportedOperationException();
        }
    
        /**
         * Attempt to unlock (for exclusive locks)
         * @param releases Used to set the number of unlocks.General incoming waitNode.arg
         * @return
         */
        public boolean tryUnlock(int releases){
            throw new UnsupportedOperationException();
        }
    
        /**
         * Attempt to acquire shared locks (for shared locks)
         * @param acquires
         * @return
         */
        public boolean tryLockShared(int acquires){
            throw new UnsupportedOperationException();
        }
    
        /**
         * Attempt to unlock (for shared locks)
         * @param releases
         * @return
         */
        public boolean tryUnLockShared(int releases){
            throw new UnsupportedOperationException();
        }
    }

2.JarryReentrantLock:

    package tech.jarry.learning.netease.locks4;
    
    /**
     * @Description:  Imitate ReentrantLock to achieve its basic functions and features
     * @Author: jarry
     */
    public class JarryReentrantLock {
    
        private boolean isFair;
    
        // By default, unfair locks are used to ensure efficiency (i.e. reference source code)
        public JarryReentrantLock() {
            this.isFair = false;
        }
    
        public JarryReentrantLock(boolean isFair) {
            this.isFair = isFair;
        }
    
        private CommonMask commonMask = new CommonMask(){
    
            @Override
            public boolean tryLock(int acquires){
                if (isFair){
                    return tryFairLock(acquires);
                } else {
                    return tryNonFairLock(acquires);
                }
            }
    
            private boolean tryFairLock(int acquires){
                // Here's a simple comment on how to achieve a fair lock. The key is that when a new thread arrives, it no longer tries to acquire the lock directly, but instead crashes directly into the queue (the queue is empty and has the same destination for different purposes).
                // 1. Determine if a read lock (shared lock) is occupied
                if (readCount.get() == 0){
                    // 2. Determine if a write lock (exclusive lock) is occupied
                    int writeCountValue = writeCount.get();
                    if (writeCountValue == 0){
                        // 2.1 (Core Difference) If the write lock is not occupied, the wait queue waiters need to be judged first
                        WaitNode head = waiters.peek();
                        if (head !=null && head.thread == Thread.currentThread()){
                            if (writeCount.compareAndSet(writeCountValue,writeCountValue+acquires)){
                                owner.set(Thread.currentThread());
                                return true;
                            }   // Competition loses and goes straight back to false
                        }
                    } else {
                        // 2.2 If the write lock is already occupied, determine if it is held by the current thread and whether reentry is performed
                        if (owner.get() == Thread.currentThread()){
                            // If the thread holding the exclusive lock is the current thread, then there is no need to change owner or CAS, just modify the value of writeCount
                            writeCount.set(writeCountValue + acquires);
                            return true;
                        }
                    }
                }
                // If the above operation fails, false is returned, indicating that the competing lock has failed
                return false;
            }
    
            private boolean tryNonFairLock(int acquires){
                if (readCount.get() == 0){
                    int writeCountValue = writeCount.get();
                    if (writeCountValue == 0){
                        if (writeCount.compareAndSet(writeCountValue,writeCountValue+acquires)){
                            owner.set(Thread.currentThread());
                            return true;
                        }
                    } else {
                        if (Thread.currentThread() == owner.get()){
                            writeCount.set(writeCountValue+acquires);
                            return true;
                        }
                    }
                }
                return false;
            }
    
            @Override
            public boolean tryUnlock(int releases) {
                if (owner.get() != Thread.currentThread()){
                    throw new IllegalMonitorStateException();
                }
                int writeCountValue = writeCount.get();
                writeCount.set(writeCountValue-releases);
                if (writeCount.get() == 0){
                    owner.compareAndSet(Thread.currentThread(),null);
                    return true;
                } else {
                    return false;
                }
            }
    
            // Other related operations, such as sharing locks, are not performed.If forced, only UnsupportedOperationException will occur
        };
    
        public void lock() {
            commonMask.lock();
        }
    
        public void unlock() {
            commonMask.unlock();
        }
    
        public boolean tryLock(int acquire) {
            return commonMask.tryLock(acquire);
        }
    
        private boolean tryUnlock(int release) {
            return commonMask.tryUnlock(release);
        }
    }

3.JarryReadWriteLock:

    package tech.jarry.learning.netease.locks4;
    
    /**
     * @Description: 
     * @Author: jarry
     */
    public class JarryReadWriteLock {
    
        private CommonMask commonMask = new CommonMask(){
    
            @Override
            public boolean tryLock(int acquires){
                if (readCount.get() == 0){
                    int writeCountValue = writeCount.get();
                    if (writeCountValue == 0){
                        if (writeCount.compareAndSet(writeCountValue,writeCountValue+acquires)){
                            owner.set(Thread.currentThread());
                            return true;
                        }
                    } else {
                        if (Thread.currentThread() == owner.get()){
                            writeCount.set(writeCountValue+acquires);
                            return true;
                        }
                    }
                }
                return false;
            }
    
            @Override
            public boolean tryUnlock(int releases) {
                if (owner.get() != Thread.currentThread()){
                    throw new IllegalMonitorStateException();
                }
                int writeCountValue = writeCount.get();
                writeCount.set(writeCountValue-releases);
                if (writeCount.get() == 0){
                    owner.compareAndSet(Thread.currentThread(),null);
                    return true;
                } else {
                    return false;
                }
            }
    
            @Override
            public boolean tryLockShared(int acquires) {
                while (true){
                    if (writeCount.get() == 0 || owner.get() == Thread.currentThread()){
                        int readCountValue = readCount.get();
                        if (readCount.compareAndSet(readCountValue, readCountValue+acquires)){
                            return true;
                        }
                    }
                    return false;
                }
            }
    
            @Override
            public boolean tryUnLockShared(int releases) {
                while (true){
                    int readCountValue = readCount.get();
                    int readCountNext = readCountValue - releases;
                    if (readCount.compareAndSet(readCountValue,readCountNext)){
                        return readCountNext == 0;
                    }
                }
            }
        };
    
    
        /**
         * Acquire exclusive locks (for exclusive locks)
         */
        public void lock(){
            commonMask.lock();
        }
    
        /**
         * Unlock (for exclusive locks)
         */
        public void unlock(){
            commonMask.unlock();
        }
    
        /**
         * Attempt to acquire an exclusive lock (for exclusive locks)
         * @param acquires Number of times to lock.Typically, waitNode.arg is passed in (1 in this code).Why don't you use a constant 1 and you don't know?
         * @return
         */
        public boolean tryLock(int acquires){
            return commonMask.tryLock(acquires);
        }
    
        /**
         * Attempt to unlock (for exclusive locks)
         * @param releases Used to set the number of unlocks.General incoming waitNode.arg
         * @return
         */
        public boolean tryUnlock(int releases){
            return commonMask.tryUnlock(releases);
        }
    
        /**
         * Acquire shared locks (for shared locks)
         */
        public void lockShared(){
            commonMask.lockShared();
        }
    
        /**
         * Unlock (for shared locks)
         */
        public boolean unLockShared(){
            return commonMask.unLockShared();
        }
    
        /**
         * Attempt to acquire shared locks (for shared locks)
         * @param acquires
         * @return
         */
        public boolean tryLockShared(int acquires){
            return tryLockShared(acquires);
        }
    
        /**
         * Attempt to unlock (for shared locks)
         * @param releases
         * @return
         */
        public boolean tryUnLockShared(int releases){
            return commonMask.tryUnLockShared(releases);
        }
    
    }

In this way, it looks good.However, there are two problems.On the one hand, the two Locks do not implement the Lock and ReadWriteLock interfaces as the actual source code does.On the other hand, JarryReadWriteLock does not acquire the corresponding Lock (such as ReadLock and WriteLock) as the actual source code does, and then perform the corresponding lock operation (in fact, implement the ReadWriteLock interface).

So let's transform it.The final version of the James Ghost CommonMask-JameAQS is used directly here.Use your own AQS here because your own AQS has some key notes.

Fourth, Simple JUC (Version 4):

1.JarryAQS:

    package tech.jarry.learning.netease.locks6;
    
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.concurrent.atomic.AtomicReference;
    import java.util.concurrent.locks.LockSupport;
    
    /**
     * @Description: 
     * @Author: jarry
     */
    public class JarryAQS {
    
        volatile AtomicInteger readCount = new AtomicInteger(0);
        AtomicInteger writeCount = new AtomicInteger(0);
        AtomicReference<Thread> owner = new AtomicReference<>();
        public volatile LinkedBlockingQueue<WaitNode> waiters = new LinkedBlockingQueue<>();
    
        class WaitNode{
            Thread thread = null;
            // Indicates the type of lock you want to strive for.0 for write lock (exclusive lock) and 1 for read lock (shared lock)
            int type = 0;
            int arg = 0;
    
            public WaitNode(Thread thread, int type, int arg) {
                this.type = type;
                this.thread = thread;
                this.arg = arg;
            }
        }
    
        /**
         * Acquire exclusive locks (for exclusive locks)
         */
        public void lock(){
            int arg = 1;
            if (!tryLock(arg)){
                WaitNode waitNode = new WaitNode(Thread.currentThread(), 0, arg);
                waiters.offer(waitNode);
    
                while (true){
                    WaitNode headNote = waiters.peek();
                    if (headNote !=null && headNote.thread == Thread.currentThread()){
                        if (!tryLock(headNote.arg)){
                            LockSupport.park();
                        } else {
                            waiters.poll();
                            return;
                        }
                    }else {
                        LockSupport.park();
                    }
                }
            }
        }
    
        /**
         * Unlock (for exclusive locks)
         */
        public void unlock(){
            int arg = 1;
            if (tryUnlock(arg)){
                WaitNode head = waiters.peek();
                if (head == null){
                    return;
                }
                LockSupport.unpark(head.thread);
            }
        }
    
    
        /**
         * Acquire shared locks (for shared locks)
         */
        public void lockShared(){
            int arg = 1;
            if (!tryLockShared(arg)){
                WaitNode waitNode = new WaitNode(Thread.currentThread(),1,arg);
                waiters.offer(waitNode);
    
                while (true){
                    WaitNode head = waiters.peek();
                    if (head != null && head.thread == Thread.currentThread()){
                        if (tryLockShared(head.arg)){
                            waiters.poll();
    
                            WaitNode newHead = waiters.peek();
                            if (newHead != null && newHead.type == 1){
                                LockSupport.unpark(newHead.thread);
                            }
                            return;
                        } else {
                            LockSupport.park();
                        }
                    } else {
                        LockSupport.park();
                    }
                }
            }
        }
    
        /**
         * Unlock (for shared locks)
         */
        public boolean unLockShared(){
            int arg = 1;
            if (tryUnLockShared(arg)){
                WaitNode head = waiters.peek();
                if (head != null){
                    LockSupport.unpark(head.thread);
                }
                return true;
            }
            return false;
        }
    
        /**
         * Attempt to acquire an exclusive lock (for exclusive locks)
         * @param acquires
         * @return
         */
        public boolean tryLock(int acquires){
            throw new UnsupportedOperationException();
        }
    
        /**
         * Attempt to unlock (for exclusive locks)
         * @param releases Used to set the number of unlocks.General incoming waitNode.arg
         * @return
         */
        public boolean tryUnlock(int releases){
            throw new UnsupportedOperationException();
        }
    
        /**
         * Attempt to acquire shared locks (for shared locks)
         * @param acquires
         * @return
         */
        public boolean tryLockShared(int acquires){
            throw new UnsupportedOperationException();
        }
    
        /**
         * Attempt to unlock (for shared locks)
         * @param releases
         * @return
         */
        public boolean tryUnLockShared(int releases){
            throw new UnsupportedOperationException();
        }
    }

2.JarryReentrantLock:

    
    package tech.jarry.learning.netease.locks6;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    
    /**
     * @Description:  Imitate ReentrantLock to achieve its basic functions and features
     * @Author: jarry
     */
    public class JarryReentrantLock implements Lock {
    
        private boolean isFair;
    
        // By default, unfair locks are used to ensure efficiency (i.e. reference source code)
        public JarryReentrantLock() {
            this.isFair = false;
        }
    
        public JarryReentrantLock(boolean isFair) {
            this.isFair = isFair;
        }
    
        private JarryAQS jarryAQS = new JarryAQS(){
    
            @Override
            // In the source code, FairSync and NonFairSync are implemented as two separate extended Sync classes.That will be more elegant, less coupled, and more scalable (and the actual source code will have more parts to override than this custom demo, where only one tryLock method needs to be overridden)
            public boolean tryLock(int acquires){
                if (isFair){
                    return tryFairLock(acquires);
                } else {
                    return tryNonFairLock(acquires);
                }
            }
    
            private boolean tryFairLock(int acquires){
                // Here's a simple comment on how to achieve a fair lock. The key is that when a new thread arrives, it no longer tries to acquire the lock directly, but instead crashes directly into the queue (the queue is empty and has the same destination for different purposes).
                // 1. Determine if a read lock (shared lock) is occupied
                if (readCount.get() == 0){
                    // 2. Determine if a write lock (exclusive lock) is occupied
                    int writeCountValue = writeCount.get();
                    if (writeCountValue == 0){
                        // 2.1 (Core Difference) If the write lock is not occupied, the wait queue waiters need to be judged first
                        WaitNode head = waiters.peek();
                        if (head !=null && head.thread == Thread.currentThread()){
                            if (writeCount.compareAndSet(writeCountValue,writeCountValue+acquires)){
                                owner.set(Thread.currentThread());
                                return true;
                            }   // Competition loses and goes straight back to false
                        }
                    } else {
                        // 2.2 If the write lock is already occupied, determine if it is held by the current thread and whether reentry is performed
                        if (owner.get() == Thread.currentThread()){
                            // If the thread holding the exclusive lock is the current thread, then there is no need to change owner or CAS, just modify the value of writeCount
                            writeCount.set(writeCountValue + acquires);
                            return true;
                        }
                    }
                }
                // If the above operation fails, false is returned, indicating that the competing lock has failed
                return false;
            }
    
            private boolean tryNonFairLock(int acquires){
                if (readCount.get() == 0){
                    int writeCountValue = writeCount.get();
                    if (writeCountValue == 0){
                        if (writeCount.compareAndSet(writeCountValue,writeCountValue+acquires)){
                            owner.set(Thread.currentThread());
                            return true;
                        }
                    } else {
                        if (Thread.currentThread() == owner.get()){
                            writeCount.set(writeCountValue+acquires);
                            return true;
                        }
                    }
                }
                return false;
            }
    
            @Override
            /**
             *
             First, use the temporary variable c to determine if the next operation will be completely unlocked.
             If fully unlocked, first release the owner, then change count (state in source code) to 0 through setState.
             This shifts the order, but avoids the owner's atomicity problem (after all, other threads use state to determine if they can compete for locks and modify owner's).
             */
            public boolean tryUnlock(int releases) {
                if (owner.get() != Thread.currentThread()){
                    throw new IllegalMonitorStateException();
                }
                int writeCountNextValue = writeCount.get() - releases;
                boolean result = false;
                if (writeCountNextValue == 0){
                    result = true;
                    owner.set(null);
                }
                writeCount.set(writeCountNextValue);
                return result;
            }
    
            // Other related operations, such as sharing locks, are not performed.If forced, only UnsupportedOperationException will occur
        };
    
        @Override
        public void lock() {
            jarryAQS.lock();
        }
    
        @Override
        public void lockInterruptibly() throws InterruptedException {
    
        }
    
        @Override
        public boolean tryLock() {
            return jarryAQS.tryLock(1);
        }
    
        @Override
        public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
            return false;
        }
    
        @Override
        public void unlock() {
            jarryAQS.unlock();
        }
    
        @Override
        public Condition newCondition() {
            return null;
        }
    
    }

3.JarryReadWriteLock:

    
    package tech.jarry.learning.netease.locks6;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReadWriteLock;
    
    /**
     * @Description: 
     * @Author: jarry
     */
    public class JarryReadWriteLock implements ReadWriteLock {
    
        private JarryAQS jarryAQS = new JarryAQS(){
    
            @Override
            // The actual source code is through the Sync class, inheriting AQS, then Override.
            public boolean tryLock(int acquires){
                if (readCount.get() == 0){
                    int writeCountValue = writeCount.get();
                    if (writeCountValue == 0){
                        if (writeCount.compareAndSet(writeCountValue,writeCountValue+acquires)){
                            owner.set(Thread.currentThread());
                            return true;
                        }
                    } else {
                        if (Thread.currentThread() == owner.get()){
                            writeCount.set(writeCountValue+acquires);
                            return true;
                        }
                    }
                }
                return false;
            }
    
            @Override
            public boolean tryUnlock(int releases) {
                if (owner.get() != Thread.currentThread()){
                    throw new IllegalMonitorStateException();
                }
                int writeCountNextValue = writeCount.get() - releases;
                boolean result = false;
                if (writeCountNextValue == 0){
                    result = true;
                    owner.set(null);
                }
                writeCount.set(writeCountNextValue);
                return result;
            }
    
            @Override
            public boolean tryLockShared(int acquires) {
                while (true){
                    if (writeCount.get() == 0 || owner.get() == Thread.currentThread()){
                        int readCountValue = readCount.get();
                        if (readCount.compareAndSet(readCountValue, readCountValue+acquires)){
                            return true;
                        }
                    }
                    return false;
                }
            }
    
            @Override
            public boolean tryUnLockShared(int releases) {
                while (true){
                    int readCountValue = readCount.get();
                    int readCountNext = readCountValue - releases;
                    if (readCount.compareAndSet(readCountValue,readCountNext)){
                        return readCountNext == 0;
                    }
                }
            }
        };
    
    
        /**
         * Acquire exclusive locks (for exclusive locks)
         */
        public void lock(){
            jarryAQS.lock();
        }
    
        /**
         * Unlock (for exclusive locks)
         */
        public void unlock(){
            jarryAQS.unlock();
        }
    
        /**
         * Attempt to acquire an exclusive lock (for exclusive locks)
         * @param acquires Number of times to lock.Typically, waitNode.arg is passed in (1 in this code).Why don't you use a constant 1 and you don't know?
         * @return
         */
        public boolean tryLock(int acquires){
            return jarryAQS.tryLock(acquires);
        }
    
        /**
         * Attempt to unlock (for exclusive locks)
         * @param releases Used to set the number of unlocks.General incoming waitNode.arg
         * @return
         */
        public boolean tryUnlock(int releases){
            return jarryAQS.tryUnlock(releases);
        }
    
        /**
         * Acquire shared locks (for shared locks)
         */
        public void lockShared(){
            jarryAQS.lockShared();
        }
    
        /**
         * Unlock (for shared locks)
         */
        public boolean unLockShared(){
            return jarryAQS.unLockShared();
        }
    
        /**
         * Attempt to acquire shared locks (for shared locks)
         * @param acquires
         * @return
         */
        public boolean tryLockShared(int acquires){
            return tryLockShared(acquires);
        }
    
        /**
         * Attempt to unlock (for shared locks)
         * @param releases
         * @return
         */
        public boolean tryUnLockShared(int releases){
            return jarryAQS.tryUnLockShared(releases);
        }
    
        @Override
        public Lock readLock() {
            return new Lock() {
                @Override
                public void lock() {
                    jarryAQS.lockShared();
                }
    
                @Override
                public void lockInterruptibly() throws InterruptedException {
    
                }
    
                @Override
                public boolean tryLock() {
                    return jarryAQS.tryLockShared(1);
                }
    
                @Override
                public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
                    return false;
                }
    
                @Override
                public void unlock() {
                    jarryAQS.unLockShared();
                }
    
                @Override
                public Condition newCondition() {
                    return null;
                }
            };
        }
    
        @Override
        public Lock writeLock() {
            return new Lock() {
                @Override
                public void lock() {
                    jarryAQS.lock();
                }
    
                @Override
                public void lockInterruptibly() throws InterruptedException {
    
                }
    
                @Override
                public boolean tryLock() {
                    return jarryAQS.tryLock(1);
                }
    
                @Override
                public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
                    return false;
                }
    
                @Override
                public void unlock() {
                    jarryAQS.unlock();
                }
    
                @Override
                public Condition newCondition() {
                    return null;
                }
            };
        }
    }

Here, in fact, AQS, the core of JUC, has been exposed.Through this, we can grasp the core operating mechanism of AQS.Actual AQS, however, modifies WaitNodes of storage threads and uses Node to form a chain table.And through the application of head and tail, to improve efficiency.Of course, there are lockInterruptibly and others that have not been mentioned, and there are also big heads like Condition that have not been mentioned.This part will be left for future opportunities, so go deeper.

In addition, the way to improve this is given.If you want to gain a deeper understanding of the AQS source, you can expand your custom simple AQS while reading it (think about the difference between the source implementation and your own).

For example, I learned that AQS implements the number of exclusive and shared locks held simultaneously through a state.So I'm in JarryAQS, trying to implement it to get a better understanding of it.

5. Simple JUC (version X-extended state):

1.JarryAQS:

    package tech.jarry.learning.netease.locks7;
    
    import sun.misc.Unsafe;
    
    import java.lang.reflect.Field;
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.concurrent.atomic.AtomicReference;
    import java.util.concurrent.locks.LockSupport;
    
    /**
     * @Description: 
     * @Author: jarry
     */
    public class JarryAQS {
    
        static final int SHARED_SHIFT   = 16;
        static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
        static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
        static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
    
        /** Returns the number of shared holds represented in count  */
        static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
        /** Returns the number of exclusive holds represented in count  */
        static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
    
        /**
         * The synchronization state.
         */
        public volatile int state;
        private static Unsafe unsafe;
        private static long stateOffset;
        static{
            try {
                Field field = Unsafe.class.getDeclaredField("theUnsafe");
                field.setAccessible(true);
                unsafe = (Unsafe) field.get(null);
    
                Field fieldi = JarryAQS.class.getDeclaredField("state");
                stateOffset = unsafe.objectFieldOffset(fieldi);
    
            } catch (NoSuchFieldException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        protected final boolean compareAndSetState(int expect, int update) {
            // See below for intrinsics setup to support this
            return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
        }
    
    
        
        volatile AtomicInteger readCount = new AtomicInteger(0);
        AtomicInteger writeCount = new AtomicInteger(0);
    
        AtomicReference<Thread> owner = new AtomicReference<>();
        public volatile LinkedBlockingQueue<WaitNode> waiters = new LinkedBlockingQueue<>();
    
        class WaitNode{
            Thread thread = null;
            // Indicates the type of lock you want to strive for.0 for write lock (exclusive lock) and 1 for read lock (shared lock)
            int type = 0;
            int arg = 0;
    
            public WaitNode(Thread thread, int type, int arg) {
                this.type = type;
                this.thread = thread;
                this.arg = arg;
            }
        }
    
        /**
         * Acquire exclusive locks (for exclusive locks)
         */
        public void lock(){
            int arg = 1;
            if (!tryLock(arg)){
                WaitNode waitNode = new WaitNode(Thread.currentThread(), 0, arg);
                waiters.offer(waitNode);
    
                while (true){
                    WaitNode headNote = waiters.peek();
                    if (headNote !=null && headNote.thread == Thread.currentThread()){
                        if (!tryLock(headNote.arg)){
                            LockSupport.park();
                        } else {
                            waiters.poll();
                            return;
                        }
                    }else {
                        LockSupport.park();
                    }
                }
            }
        }
    
        /**
         * Unlock (for exclusive locks)
         */
        public void unlock(){
            int arg = 1;
            if (tryUnlock(arg)){
                WaitNode head = waiters.peek();
                if (head == null){
                    return;
                }
                LockSupport.unpark(head.thread);
            }
        }
    
    
        /**
         * Acquire shared locks (for shared locks)
         */
        public void lockShared(){
            int arg = 1;
            if (!tryLockShared(arg)){
                WaitNode waitNode = new WaitNode(Thread.currentThread(),1,arg);
                waiters.offer(waitNode);
    
                while (true){
                    WaitNode head = waiters.peek();
                    if (head != null && head.thread == Thread.currentThread()){
                        if (tryLockShared(head.arg)){
                            waiters.poll();
    
                            WaitNode newHead = waiters.peek();
                            if (newHead != null && newHead.type == 1){
                                LockSupport.unpark(newHead.thread);
                            }
                            return;
                        } else {
                            LockSupport.park();
                        }
                    } else {
                        LockSupport.park();
                    }
                }
            }
        }
    
        /**
         * Unlock (for shared locks)
         */
        public boolean unLockShared(){
            int arg = 1;
            if (tryUnLockShared(arg)){
                WaitNode head = waiters.peek();
                if (head != null){
                    LockSupport.unpark(head.thread);
                }
                return true;
            }
            return false;
        }
    
        /**
         * Attempt to acquire an exclusive lock (for exclusive locks)
         * @param acquires
         * @return
         */
        public boolean tryLock(int acquires){
            throw new UnsupportedOperationException();
        }
    
        /**
         * Attempt to unlock (for exclusive locks)
         * @param releases Used to set the number of unlocks.General incoming waitNode.arg
         * @return
         */
        public boolean tryUnlock(int releases){
            throw new UnsupportedOperationException();
        }
    
        /**
         * Attempt to acquire shared locks (for shared locks)
         * @param acquires
         * @return
         */
        public boolean tryLockShared(int acquires){
            throw new UnsupportedOperationException();
        }
    
        /**
         * Attempt to unlock (for shared locks)
         * @param releases
         * @return
         */
        public boolean tryUnLockShared(int releases){
            throw new UnsupportedOperationException();
        }
    }

2.JarryReentrantLock:

    package tech.jarry.learning.netease.locks7;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    
    /**
     * @Description:  Imitate ReentrantLock to achieve its basic functions and features
     * @Author: jarry
     */
    public class JarryReentrantLock implements Lock {
    
        private boolean isFair;
    
        // By default, unfair locks are used to ensure efficiency (i.e. reference source code)
        public JarryReentrantLock() {
            this.isFair = false;
        }
    
        public JarryReentrantLock(boolean isFair) {
            this.isFair = isFair;
        }
    
        // The actual source code is through the Sync class, inheriting AQS, then Override.
        private JarryAQS jarryAQS = new JarryAQS(){
    
            @Override
            // In the source code, FairSync and NonFairSync are implemented as two separate extended Sync classes.That will be more elegant, less coupled, and more scalable (and the actual source code will have more parts to override than this custom demo, where only one tryLock method needs to be overridden)
            public boolean tryLock(int acquires){
                if (isFair){
                    return tryFairLock(acquires);
                } else {
                    return tryNonFairLock(acquires);
                }
            }
    
            private boolean tryFairLock(int acquires){
                // Here's a simple comment on how to achieve a fair lock. The key is that when a new thread arrives, it no longer tries to acquire the lock directly, but instead crashes directly into the queue (the queue is empty and has the same destination for different purposes).
                // 1. Determine if a read lock (shared lock) is occupied
                if (readCount.get() == 0){
                    // 2. Determine if a write lock (exclusive lock) is occupied
                    int writeCountValue = writeCount.get();
                    if (writeCountValue == 0){
                        // 2.1 (Core Difference) If the write lock is not occupied, the wait queue waiters need to be judged first
                        WaitNode head = waiters.peek();
                        if (head !=null && head.thread == Thread.currentThread()){
                            if (writeCount.compareAndSet(writeCountValue,writeCountValue+acquires)){
                                owner.set(Thread.currentThread());
                                return true;
                            }   // Competition loses and goes straight back to false
                        }
                    } else {
                        // 2.2 If the write lock is already occupied, determine if it is held by the current thread and whether reentry is performed
                        if (owner.get() == Thread.currentThread()){
                            // If the thread holding the exclusive lock is the current thread, then there is no need to change owner or CAS, just modify the value of writeCount
                            writeCount.set(writeCountValue + acquires);
                            return true;
                        }
                    }
                }
                // If the above operation fails, false is returned, indicating that the competing lock has failed
                return false;
            }
    
            private boolean tryNonFairLock(int acquires){
                if (readCount.get() == 0){
                    int writeCountValue = writeCount.get();
                    if (writeCountValue == 0){
                        if (writeCount.compareAndSet(writeCountValue,writeCountValue+acquires)){
                            owner.set(Thread.currentThread());
                            return true;
                        }
                    } else {
                        if (Thread.currentThread() == owner.get()){
                            writeCount.set(writeCountValue+acquires);
                            return true;
                        }
                    }
                }
                return false;
            }
    
            @Override
            /**
             *
             First, use the temporary variable c to determine if the next operation will be completely unlocked.
             If fully unlocked, first release the owner, then change count (state in source code) to 0 through setState.
             This shifts the order, but avoids the owner's atomicity problem (after all, other threads use state to determine if they can compete for locks and modify owner's).
             */
            public boolean tryUnlock(int releases) {
                if (owner.get() != Thread.currentThread()){
                    throw new IllegalMonitorStateException();
                }
                int writeCountNextValue = writeCount.get() - releases;
                boolean result = false;
                if (writeCountNextValue == 0){
                    result = true;
                    owner.set(null);
                }
                writeCount.set(writeCountNextValue);
                return result;
            }
    
            // Other related operations, such as sharing locks, are not performed.If forced, only UnsupportedOperationException will occur
        };
    
        @Override
        public void lock() {
            jarryAQS.lock();
        }
    
        @Override
        public void lockInterruptibly() throws InterruptedException {
    
        }
    
        @Override
        public boolean tryLock() {
            return jarryAQS.tryLock(1);
        }
    
        @Override
        public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
            return false;
        }
    
        @Override
        public void unlock() {
            jarryAQS.unlock();
        }
    
        @Override
        public Condition newCondition() {
            return null;
        }
    
    }

3.JarryReadWriteLock:

    package tech.jarry.learning.netease.locks7;
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReadWriteLock;
    
    /**
     * @Description: 
     * @Author: jarry
     */
    public class JarryReadWriteLock implements ReadWriteLock {
    
        // The actual source code is through the Sync class, inheriting AQS, then Override.
        private JarryAQS jarryAQS = new JarryAQS(){
    
            @Override
            public boolean tryLock(int acquires){
                int stateTemp = state;
                if (sharedCount(stateTemp) == 0){
                    int writeCountValue = exclusiveCount(stateTemp);
                    if (writeCountValue == 0){
                        if (compareAndSetState(stateTemp,stateTemp+acquires)){
                            owner.set(Thread.currentThread());
                            return true;
                        }
                    } else {
                        if (Thread.currentThread() == owner.get()){
                            compareAndSetState(stateTemp,stateTemp+acquires);
                            return true;
                        }
                    }
                }
                return false;
            }
    
            @Override
            public boolean tryUnlock(int releases) {
                int stateTemp = state;
                if (owner.get() != Thread.currentThread()){
                    throw new IllegalMonitorStateException();
                }
                int writeCountNextValue = exclusiveCount(stateTemp) - releases;
                boolean result = false;
                if (writeCountNextValue == 0){
                    result = true;
                    owner.set(null);
                }
                compareAndSetState(stateTemp,stateTemp - releases);
                return result;
            }
    
            @Override
            public boolean tryLockShared(int acquires) {
                while (true){
                    int stateTemp = state;
                    if (exclusiveCount(stateTemp) == 0 || owner.get() == Thread.currentThread()){
                        if (compareAndSetState(stateTemp, stateTemp+SHARED_UNIT*acquires)){
                            return true;
                        }
                    }
                    return false;
                }
            }
    
            @Override
            public boolean tryUnLockShared(int releases) {
                while (true){
                    int stateTemp = state;
                    int readCountValue = sharedCount(stateTemp);
                    int readCountNext = readCountValue - releases;
                    if (compareAndSetState(stateTemp, stateTemp-SHARED_UNIT*readCountNext)){
                        return readCountNext == 0;
                    }
                }
            }
        };
    
    
        /**
         * Acquire exclusive locks (for exclusive locks)
         */
        public void lock(){
            jarryAQS.lock();
        }
    
        /**
         * Unlock (for exclusive locks)
         */
        public void unlock(){
            jarryAQS.unlock();
        }
    
        /**
         * Attempt to acquire an exclusive lock (for exclusive locks)
         * @param acquires Number of times to lock.Typically, waitNode.arg is passed in (1 in this code).Why don't you use a constant 1 and you don't know?
         * @return
         */
        public boolean tryLock(int acquires){
            return jarryAQS.tryLock(acquires);
        }
    
        /**
         * Attempt to unlock (for exclusive locks)
         * @param releases Used to set the number of unlocks.General incoming waitNode.arg
         * @return
         */
        public boolean tryUnlock(int releases){
            return jarryAQS.tryUnlock(releases);
        }
    
        /**
         * Acquire shared locks (for shared locks)
         */
        public void lockShared(){
            jarryAQS.lockShared();
        }
    
        /**
         * Unlock (for shared locks)
         */
        public boolean unLockShared(){
            return jarryAQS.unLockShared();
        }
    
        /**
         * Attempt to acquire shared locks (for shared locks)
         * @param acquires
         * @return
         */
        public boolean tryLockShared(int acquires){
            return tryLockShared(acquires);
        }
    
        /**
         * Attempt to unlock (for shared locks)
         * @param releases
         * @return
         */
        public boolean tryUnLockShared(int releases){
            return jarryAQS.tryUnLockShared(releases);
        }
    
        @Override
        public Lock readLock() {
            return new Lock() {
                @Override
                public void lock() {
                    jarryAQS.lockShared();
                }
    
                @Override
                public void lockInterruptibly() throws InterruptedException {
    
                }
    
                @Override
                public boolean tryLock() {
                    return jarryAQS.tryLockShared(1);
                }
    
                @Override
                public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
                    return false;
                }
    
                @Override
                public void unlock() {
                    jarryAQS.unLockShared();
                }
    
                @Override
                public Condition newCondition() {
                    return null;
                }
            };
        }
    
        @Override
        public Lock writeLock() {
            return new Lock() {
                @Override
                public void lock() {
                    jarryAQS.lock();
                }
    
                @Override
                public void lockInterruptibly() throws InterruptedException {
    
                }
    
                @Override
                public boolean tryLock() {
                    return jarryAQS.tryLock(1);
                }
    
                @Override
                public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
                    return false;
                }
    
                @Override
                public void unlock() {
                    jarryAQS.unlock();
                }
    
                @Override
                public Condition newCondition() {
                    return null;
                }
            };
        }
    }

Sixth, summary:

If you come from ReentrantLock, step by step, and manually come here, then your knowledge of AQS has a very solid foundation.If you can learn by comparing the source code (realizing it yourself and understanding how the source solves related problems) during the learning process, you will have a good understanding of AQS.Even if there are deficiencies, it is only the accumulation of AQS reading.

Posted by eddie21leeds on Wed, 11 Dec 2019 18:49:38 -0800