ReentrantLockd's unfair lock lock method realizes source code analysis

//ReetrantLock source code analysis:
 Lock lock = new ReentrantLock();
      try {
    	   lock.lock();
    	   ....doSomething
	} finally {
		lock.unlock();
	}
    //Start with our most commonly used lock() method. Looking at the implementation of lock from the unfair mode,
    public void lock() {
        sync.lock(); //Delegate to sync object implementation.
    }
    //implementation of sync
      abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        //The abstract method is mainly to implement it
        abstract void lock();

        /**
         * Performs non-fair tryLock.  tryAcquire is
         * implemented in subclasses, but both need nonfair
         * try for trylock method.
         */
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

        protected final boolean tryRelease(int releases) {
            int c = getState() - releases;
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

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

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

        // Methods relayed from outer class

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

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

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

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

    sync in unfair mode:
     static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        //OK find the implementation method
        final void lock() {
	  //Use the CAS mechanism to set the state value to 1, that is, if the state in the AbstractQueuedSynchronizer is 0, set it to 1
            if (compareAndSetState(0, 1))
	        //Success. Set the current thread as an exclusive thread
                setExclusiveOwnerThread(Thread.currentThread());
            else
	       // Attempt to get the object exclusively.
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

     //Call the acquire method in AbstractQueuedSynchronizer
     public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
    //call tryAcquire
    protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }


  //then call this method
  final boolean nonfairTryAcquire(int acquires) {
            // get the current thread
            final Thread current = Thread.currentThread();
            int c = getState();
	    //Get the current state, if it is 0, you can acquire the lock (other threads have released resources).
            if (c == 0) {
	       
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
	    //The current thread is the exclusive thread. Thread reentrancy.
            else if (current == getExclusiveOwnerThread()) {
	       //state value +1. The hesitation is entered by the same thread and holds the lock, so the CAS mechanism can be used to increase the number.
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

//If the above method fails to acquire the lock, return false to continue execution:
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
First look at addWaiter():

private Node addWaiter(Node mode) {
        //Create a node node. mode is the mode currently set for the mode is Node.EXCLUSIVE exclusive lock;
        Node node = new Node(Thread.currentThread(), mode);
        // Quick enqueue.
        Node pred = tail;
        if (pred != null) {
            node.prev = before;
	    //Everything goes well and no other threads are enqueued at this time with CAS.
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
	//Unfortunately, other threads have modified the tail node of the queue. Then loop into the queue
        enq(node);
        return node;
    }
    
 private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
	    //current queue is empty
            if (t == null) { //Initialize a node
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
	        //try infinitely until node joins the queue
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

Then there is the acquireQueued() method:
final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
		//If the previous node of node is the head node and try to acquire the lock again successfully
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
		
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt ())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }


     private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            /*
             If the previous node is signal and is waiting to wake up, then the current node can be suspended.
             */
            return true;
        if (ws > 0) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
	     It means that the previous thread has been cancelled, and these nodes have been dequeued in a loop.
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * waitStatus must be 0 or PROPAGATE.  Indicate that we
             * need a signal, but don't park yet.  Caller will need to
             * retry to make sure it cannot acquire before parking.
             */
	     //Set the status of the precursor node to signal
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

     private final boolean parkAndCheckInterrupt() {
        // Suspend the current thread
        LockSupport.park(this);
	//Return whether the current thread is interrupted
        return Thread.interrupted();
    }

// cancel the thread
    private void cancelAcquire(Node node) {
       
        if (node == null)
            return;

        node.thread = null;

      
        Node pred = node.prev;
        while (pred.waitStatus > 0)
            node.prev = pred = pred.prev;

        Node predNext = pred.next;

       
        node.waitStatus = Node.CANCELLED;

       
        if (node == tail && compareAndSetTail(node, pred)) {
            compareAndSetNext (pred, predNext, null);
        } else {
          
            int ws;
            if (pred != head &&
                ((ws = pred.waitStatus) == Node.SIGNAL ||
                 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
                pred.thread != null) {
                Node next = node.next;
                if (next != null && next.waitStatus <= 0)
                    compareAndSetNext (pred, predNext, next);
            } else {
                unparkSuccessor(node);
            }

            node.next = node; // help GC
        }
    }



Summary: All steps of the lock method are implemented:
   1. The lock is not acquired.
   2. Try to acquire the lock again.
   3. If the lock acquisition fails, create a node to join the queue. If the current queue is empty, create an empty node as the head and tail. If it is not empty, use the CAS mechanism to loop infinitely until it joins the queue.
   4. Determine whether the current thread is the second node. If so, try to acquire the lock again (successfully set the current node as the head node). If not, determine whether the current thread needs to be suspended.
     If the previous node state of the current node is singal, suspend the current thread. If it is not signal, all nodes with waitstatus>0 (indicating that the node is interrupted) in front of the current node are removed from the queue. The next time you enter this method, set the value of the predecessor node to singal.


So all threads will block at the fourth step. until the thread is woken up. The thread that leaves first must be the second node and be able to acquire the lock.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326523992&siteId=291194637