Article reference: Miss Liu’s source code class

The complete code

MiniLock:

/ * * *@author csp
 * @dateThe 2021-05-01 * /
public interface MiniLock {
    /** * lock */
    void lock(a);

    /** * release lock */
    void unlock(a);
}
Copy the code

\

MiniReentrantLock:

/ * * *@author csp
 * @date2021-05-01 * MiniReentrantLock is an exclusive lock, fair lock, reentrant lock */
public class MiniReentrantLock implements MiniLock {
    /** * lock state: =0, >0, * what is locked? -> Lock the thread resource */
    private volatile int state;

    /** * The thread currently owning the lock: * What is exclusive mode? * Only one thread can hold the lock at a time. Other threads will block if they do not acquire the lock */
    private Thread exclusiveOwnerThread;

    * 1. Head points to the node at the head of the queue -> the thread corresponding to the head node, which is the thread currently holding the lock! * 2. Tail Points to the node */ at the end of the queue
    /** * Queue header */
    private Node head;
    /** * end of queue */
    private Node tail;

    /** * Blocked threads are encapsulated as Node nodes and placed in FIFO queues */
    static final class Node {
        // Front node reference
        Node prev;
        // Post-node reference
        Node next;
        // Encapsulate the thread
        Thread thread;

        public Node(a) {}public Node(Thread thread) {
            this.thread = thread;
        }

        public Node(Node prev, Node next, Thread thread) {
            this.prev = prev;
            this.next = next;
            this.thread = thread; }}/** * Get the lock: * if the current lock is occupied, it will block the calling thread until it preempts the lock. * 

* lock Lock process: * Scenario 1: when the thread executes, the current state == 0, the thread can directly grab the lock */ scenario 2: when the thread executes, the current state == 0, the thread needs to enqueue */

@Override public void lock(a) { // make the current thread compete for resources // State = 1 when the lock is first acquired // State = n when the lock is acquired NTH time acquire(1); } /** * The method by which the current thread competes for resources; * 2. Try to obtain the lock, obtain the failure, block the current thread * *@param arg */ private void acquire(int arg) { // Failed to obtain the lock if(! tryAcquire(arg)) {// More complex logic comes ~ // Add the current thread to the blocking queue Node node = addWaiter(); // Once enqueued, the current thread continues to compete for resources until it successfully acquires the lock acquireQueued(node, arg); } // Attempt to acquire the lock succeeds. The current exclusive lock thread exclusiveOwnerThread and related properties execute ~ in tryAcquire } /** * What do I need to do when AN attempt to preempt the lock fails? * 1. Wrap the current thread as node and add it to the blocking queue * 2. Park the current thread so that it is suspended and waiting to be woken up * * What do YOU need to do when woken up? * 1. Check whether the current node is head.next * (head.next is a thread with preemption permission, and no other node has preemption permission) * 2. Preemption * If preemption succeeds, the current node is set to head, the old head is removed from the queue, and the business layer is returned * * if preemption fails, the current thread will continue to park, waiting to be woken up * * =====> * 1. Logical addWaiter() * 2 to add the current thread to the blocking queue. The current thread contention resource logic acquireQueued() */ /** * Add the current thread to the blocking queue: head is the lock holding node * addWaiter() after completion, ensure that the current thread has been queued successfully! * *@returnReturns the current thread's Node */ private Node addWaiter(a) { // Create the current thread node node Node newNode = new Node(Thread.currentThread()); // How to join the team? // 1. Find the pred of newNode // 2. Update newNode.prev = pred // 3.CAS updates tail to newNode // 4. Update pred.next = newNode // In case 1, there are already waiting nodes in the queue (not empty), and the current node is not the first node to join the queue Node pred = tail; if(pred ! =null) { newNode.prev = pred; // If the condition is true, the current thread is queued successfully! if (compareAndSetTail(pred, newNode)) { pred.next = newNode; returnnewNode; }}// There are two cases: // 1.tail == null The queue is empty // 2. Cas failed to set the current newNode to tail because another thread beat it to tail // Spin into the team, only if the team successfully completed the spin: eng(newNode); return newNode; } /** * causes the current thread to compete for resources until it successfully acquires the lock@param node * @param arg */ private void acquireQueued(Node node, int arg) { // Only when the current node successfully acquired the lock will the spin exit: for(; ;) {// Under what circumstances can a node attempt to acquire a lock after being awakened? // Only if the current node is a successor of the head node. Node pred = node.prev; // the head node is the node currently entitled to hold the lock if (pred == head /* If the condition is true, the current node has preemption permission */ && tryAcquire(arg) /* Try to get the lock */) { // Enter if, the current thread contention lock successfully! // What do I need to do next? // 1. Set the current head to the node of the current thread // 2. Assist the original head to exit the team setHead(node); pred.next = null;// help GC return; } // If there is no lock preemption permission, the current thread suspends and continues to wait... // park... Thread hanging System.out.println("Thread:" + Thread.currentThread().getName() + "Hang up!); LockSupport.park(); System.out.println("Thread:" + Thread.currentThread().getName() + "Wake up!); }}Tail == null is an empty queue * 2. Cas failed to set the current newNode to tail, which was preempted by another thread */ private void eng(Node node) { / / spin ~ for(; ;) {The current thread is the first thread to fail to preempt the lock // The thread that currently holds the lock (note: the tryAcquire method directly acquirement the thread that holds the lock. // The thread holding the lock is not set to any node. // As the thread's first afterdrive, next needs to clean up its ass (add a node to the thread holding the lock and set it to the head of the blocking queue) // the head node at any time represents the thread currently holding the lock. if (tail == null) { // If the compareAndSetHead condition is true, the current thread gave the current thread the lock, and the head operation succeeded! if (compareAndSetHead(new Node())) { // tail = head indicates that the current queue has only one element, where the current thread holding the lock is put into the blocking queue and is head ~ tail = head; // The next time you enter the loop, the blocking queue is not empty, and head is the thread node holding the lock.}}else { // Add node to queue if node is already in queue // How to join the team? Same logic as addWaiter method // 1. Find the pred of newNode // 2. Update newNode.prev = pred // 3.CAS updates tail to newNode // 4. Update pred.next = newNode // The queue already has a waiting node (not empty). The current node is not the first node to join the queue Node pred = tail; if(pred ! =null) { node.prev = pred; // If the condition is true, the current thread is queued successfully! if (compareAndSetTail(pred, node)) { pred.next = node; // If you join the team, you must return the end of the infinite for loop ~ return; } } } } } /** * Attempts to acquire the lock without blocking the thread **@param arg * @returnTrue - > try to obtain success | false lock - > attempt failed to get the lock * / private boolean tryAcquire(int arg) { // If the current locked state is 0 if (state == 0) { // Condition 1:! HasQueuePredecessor () : indicates if there are no waiting threads in front of the current thread (to ensure fairness) // Condition 2: compareAndSetState(0, ARG) : State was successfully modified using CAS // If conditions 1 and 2 are true, the current thread succeeded in grabbing the lock. if(! hasQueuePredecessor() && compareAndSetState(0, arg)) { // Lock grab succeeded: // 1. You need to set the exclusiveOwnerThread to the current thread in the IF block this.exclusiveOwnerThread = Thread.currentThread(); // Succeeded in obtaining the lock return true; } // If the current thread is the thread holding the lock and state > 0 } else if (Thread.currentThread() == this.exclusiveOwnerThread) { // Is there concurrency in this if judgment? Does not exist -> because only the currently locked thread has permission to change state // Get the lock status of the current thread int s = getState(); s = s + arg; // Cross the line... (omitted), in ReentrantLock, there must be an out-of-bounds judgment here // Assign s to state to update the lock state this.state = s; // Succeeded in obtaining the lock return true; } // Failed to obtain the lock: // 1.CAS failed to lock and there is a waiting thread in front of the current thread // 2. State > 0 and the current thread is not the thread that occupies the lock return false; } /** * Method call chain: * lock -> acquire -> tryAcquire -> hasQueuedPredecessor (ps: state is 0, that is, the current lock is in the no master state..) * / /** * Check whether the FIFO queue is empty: **@returnTrue - > said in front of the current thread is waiting for the thread | false - > said the current thread is waiting for the thread * / private boolean hasQueuePredecessor(a) { Node h = head; Node t = tail; Node s; // When is false returned? // 1. The current queue is empty // 2. The current thread is the head. Next node thread, head // Condition 1: h! = t // Yes: the current queue already has node H == t == null 2. h == t == head The first thread that fails to acquire the lock will create a head node for the thread that currently holds the lock. / / condition 2: ((s = h.n ext) = = null | | s.t hread! = Thread.currentThread()) // Rule out several cases: // condition 2.1 :(s = h.ext) == null // In extreme cases, the first thread that fails to acquire the lock will create a new head for the thread that holds the lock, and then automatically join the queue. // The thread will return true if the head. Next node is present returnh ! = t && ((s = h.next) ==null|| s.thread ! = Thread.currentThread()); }/** * release lock */ @Override public void unlock(a) { / / releases the lock release(1); } /** * Release lock method **@param arg */ private void release(int arg) { // The tryRelease condition is true: the thread has released the lock completely // What can I do for you? // There are several sleeping threads in the blocking queue. Should I wake up a thread? if (tryRelease(arg)) { // Get the head node Node head = this.head; // You have to know, is there any waiting? // If head. Next == null, there is no waiting, // If head. Next! = null indicates that there is a waiting thread to wake up if(head.next ! =null) { // Wake up the head. Next nodeunparkSuccessor(head); }}}/** * wakes up the thread node **@param node */ private void unparkSuccessor(Node node) { // node after head Node s = node.next; if(s ! =null&& s.thread ! =null) { // The waiting thread wakes upLockSupport.unpark(s.thread); }}/ * * * in fact completely releases the lock methods * completely releases the lock is successful, it returns true | otherwise, shows the current state > 0, return false. * / private boolean tryRelease(int arg) { int c = getState() - arg; if(getExclusiveOwnerThread() ! = Thread.currentThread()) {throw new RuntimeException("No! You must getLock!"); } // What if I go here? Is there concurrency? Only one thread, the ExclusiveOwnerThread, is going to come in here. // If the current thread holds the lock, the lock has been released completely. if (c == 0) { // What needs to be done? // 1.ExclusiveOwnerThread set to null // 2. Set state == 0 this.exclusiveOwnerThread = null; this.state = c; return true; } // state is not equal to 0, cannot release the lock, return false this.state = c; return false; } private void setHead(Node node) { this.head = node; // Why is thread null? The node is already the thread that acquired the lock node.thread = null; node.prev = null; } public int getState(a) { return state; } public Thread getExclusiveOwnerThread(a) { return exclusiveOwnerThread; } public Node getHead(a) { return head; } public Node getTail(a) { return tail; } /** * 基于CAS,得到state、head、tail属性的setter方法 */ private static final Unsafe unsafe; private static final long stateOffset; private static final long headOffset; private static final long tailOffset; static { try { Field f = Unsafe.class.getDeclaredField("theUnsafe"); f.setAccessible(true); unsafe = (Unsafe) f.get(null); stateOffset = unsafe.objectFieldOffset (MiniReentrantLock.class.getDeclaredField("state")); headOffset = unsafe.objectFieldOffset (MiniReentrantLock.class.getDeclaredField("head")); tailOffset = unsafe.objectFieldOffset (MiniReentrantLock.class.getDeclaredField("tail")); } catch (Exception ex) { throw newError(ex); }}private final boolean compareAndSetHead(Node update) { return unsafe.compareAndSwapObject(this, headOffset, null, update); } private final boolean compareAndSetTail(Node expect, Node update) { return unsafe.compareAndSwapObject(this, tailOffset, expect, update); } protected final boolean compareAndSetState(int expect, int update) { return unsafe.compareAndSwapInt(this, stateOffset, expect, update); }}Copy the code