Not fair lock

  • The basic concept
  • Get an unfair lock
    • lock
    • acquire
      • tryAcquire
        • nonfairTryAcquire
  • Release an unfair lock
    • unlock
    • release
      • tryRelease
      • unparkSuccessor
  • conclusion

The basic concept

  • Unfair lock and fair lock in the method of obtaining the lock, the process is the same, the difference is different from the mechanism of obtaining the lock

    • Unfair lock Every time a lock is attempted to be obtained, the unfair policy is adopted. Ignoring the waiting queue, the lock is directly obtained. If the lock is idle, the lock status can be obtained, and the lock is obtained
    • Fair lock Every attempt to obtain a lock adopts a fair policy. The lock is obtained in sequence according to the waiting queue

Get an unfair lock

lock

  • Lock () is implemented in the NonfairSync class of ReentrantLock:
		final void lock(a) {
            if (compareAndSetState(0.1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
Copy the code
  • The lock() method first determines whether the lock is idle by calling compareAndSetState(0, 1)

    • If it is idle, the current thread can acquire the lock directly

    • If it is not idle, acquire the lock by calling Acquire (1)

      • CompareAndSet () is the CAS function that compares and sets the status of the current lock
  • SetExclusiveOwnerThread (thread.currentthread ()) sets the currentThread as the lock holder

  • Comparison of fair lock() and unfair lock() methods:

    • Call acquire(1) directly from the lock() method of a fair lock
    • The lock() method of an unfair lock first determines whether the current lock is in an idle state, and if so, acquires the lock directly

acquire

  • Acquire () in AQS:
	public final void acquire(int arg) {
        if(! tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }Copy the code
  • The current thread first tries to acquire the lock through tryAcquire() and returns if it succeeds. If it fails to get the lock, it enters the wait queue and then gets the lock

  • If an attempt fails, the current thread will add the current thread to the non-blocking FIFO queue CLH queue via addWaiter(Node.exclusive)

  • Call the acquireQueued() method to obtain the lock:

    • The current thread will wait for all previous threads in the CLH queue to execute and release the lock before it can acquire the lock and return
    • If the current thread has been interrupted while waiting, selfInterrupt() is called to generate an interrupt of its own
  • Comparison of acquire() method with fair lock and acquire() method with non-fair lock:

    • Fair and unfair locks differ only in the implementation of the tryAcquire() method, that is, the mechanism for trying to acquire the lock, that is, the strategy for acquiring the lock

tryAcquire

  • TryAcquire () for an unfair lock is implemented in the NonfairSync class of the ReentrantLock class:
  	 protected final boolean tryAcquire(int acquires) {
          return nonfairTryAcquire(acquires);
      }
Copy the code
  • Comparison of tryAcquire() methods with fair locks and tryAcquire() methods with non-fair locks:

    • When the tryAcquire() method of a fair lock attempts to acquire the lock, it determines whether the current thread is in the head of the CLH queue, even if the lock is not held by any thread. The lock is only acquired if the current thread is in the head of the CLH queue
    • The tryAcquire() method of an unfair lock attempts to acquire the lock as long as it is not held by any thread

nonfairTryAcquire

  • NonfairTryAcquire () is implemented in the Sync class of ReentrantLock:
		final boolean nonfairTryAcquire(int acquires) {
			// Get the current thread
            final Thread current = Thread.currentThread();
            // Get the lock status
            int c = getState();
            if (c == 0) {
            	/* * c == 0 indicates that the lock is not held by any thread * if the lock is not held by any thread, use the CAS function to set the lock status to acquires * and set the current thread as the lock holder */
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true; }}else if (current == getExclusiveOwnerThread()) {
            	// Update the status of the lock if it is already held by the current thread lock
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
Copy the code
  • NonfairTryAcquire () attempts to acquire a lock:

    • If the lock is not held by any thread, the CAS function sets the lock status to acquires and sets the current thread as the lock holder, returning true
    • If the current thread owns the lock, the state of the lock is updated, returning true
    • If neither of the above conditions exists, the attempt to acquire the lock fails and false is returned

Release an unfair lock

unlock

  • Unlock () is implemented in ReentrantLock:
public void unlock(a) {
	sync.release(1);
}
Copy the code
  • Unlock () is a lock release function, which is implemented through the release() function in AQS

  • The value of 1 is the parameter of the lock status

    • For an exclusive lock, the status value is 0 when the lock is in the reachable state
    • For an exclusive lock, the status value is 1 when the lock is first acquired by a thread
  • Because fair locks are reentrant locks, each time the lock is released for the same thread, the lock state decreases by one

release

  • Release () implemented in AQS:
	public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if(h ! =null&& h.waitStatus ! =0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
Copy the code
  • TryRelease () is first called in release() to try to release the lock held by the current thread. On success, wake up subsequent waiting threads and return true. On failure, return false

tryRelease

  • TryRelease () is implemented in the Sync class of the ReentrantLock class:
		protected final boolean tryRelease(int releases) {
			// Define the state after the lock is released
            int c = getState() - releases;
            // If the current thread is not the lock holder, an exception is thrown
            if(Thread.currentThread() ! = getExclusiveOwnerThread())throw new IllegalMonitorStateException();
            boolean free = false;
            if (c == 0) {
            	// If the current lock has been released by the current thread, set the lock holder to NULL. The lock is now available
                free = true;
                setExclusiveOwnerThread(null);
            }
            // Set the lock state of the current thread after the lock is released
            setState(c);
            return free;
        }
Copy the code
  • TryRelease () attempts to release the current lock

    • If the current thread is not the lock holder, an exception is thrown
    • After the current thread releases the lock, the current thread’s ownership status of the lock is 0, and the current thread releases the lock completely. Set the owner of the lock to NULL, which is the reachable state of the lock, and update the status of the current thread to 0

unparkSuccessor

  • After the current thread successfully releases the lock in release(), its successors wake up
  • According to the first-in, first-out FIFO principle of CLH queue, the current thread must be the head node. If the CLH queue is not empty, the next waiting thread will be woken up
	private void unparkSuccessor(Node node) {
        // Get the state of the current thread
        int ws = node.waitStatus;
        if (ws < 0)
        	// If the status is negative, set the status value to 0
            compareAndSetWaitStatus(node, ws, 0);

        /* * Gets the valid successor of the current node * if it is invalid, use the for loop to start from the end * where valid refers to the corresponding state of the successor node */
        
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for(Node t = tail; t ! =null&& t ! = node; t = t.prev)if (t.waitStatus <= 0)
                    s = t;
        }
        // Wake up the thread corresponding to the subsequent node
        if(s ! =null)
            LockSupport.unpark(s.thread);
    }
Copy the code
  • The function of the unparksucceeded () is to wake up the successors of the current thread
  • After the subsequent thread wakes up, it can continue to acquire the lock and resume running

conclusion

  • The difference between a fair lock and an unfair lock is the mechanism by which the lock is acquired:

    • Fair lock Attempts to acquire the lock only if the current thread is waiting on the head of the queue in CLH
    • When an unfair location attempts to acquire a lock, as long as the lock is idle and not held by other threads, the lock is acquired directly, regardless of the order in which the current thread is in the CLH wait queue