Reentrant lock already

As the name suggests: supports reentrant locks, synchronized supports reentrant locks. It means that a thread can repeatedly acquire the same lock object or the same thread can repeatedly lock the resource.

ReentrantLock also supports fair and unfair lock selection. The default value is unfair lock. The efficiency of fair lock is lower than that of unfair lock.

Fair lock

When there are multiple threads waiting in the synchronization queue, the thread that has waited the longest gets the lock first.

Not fair lock

The opposite of a fair lock is an unfair lock, regardless of the length of the wait.

Implement reentrant lock logic

Taking fair lock as an example, the main ideas are as follows:

  • The thread acquires the lock again. The lock needs to identify whether the current thread that wants to acquire the lock is the current thread that has acquired the lock. If so, the lock is successfully acquired
  • The final release of the lock. The thread has repeatedly acquired the lock n times, and then after releasing the lock for the NTH time, other threads can acquire the lock, so it needs to count when locking. The count decrement when the lock is released. When the count equals 0, the lock is fully released.

ReentrantLock combines custom synchronizers to acquire and release locks. ** NonfairSync#lock

final void lock(a) {
    if (compareAndSetState(0.1))// Try to get the lock
        // Set the current thread as the holder of the exclusive lock
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}
Copy the code

The final call to Sync#nonfairTryAcquire(int acquires) is the semantic implementation of the reentrant lock

final boolean nonfairTryAcquire(int acquires) {
    // Get the current thread
    final Thread current = Thread.currentThread();
    // Get the status of the current lock
    int c = getState();
    if (c == 0) {// If it is in the initial state, no thread has acquired the lock
        // Perform the CAS lock operation
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true; }}else if (current == getExclusiveOwnerThread()) {
        // If the current thread is the thread that acquired the exclusive lock, (reentrant lock)
        // Add up the state count
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    // Return false if it is not a reentrant lock
    return false;
}
Copy the code

Lock release logic

// Release lock, fair lock and unfair lock use this one
//Sync custom synchronizer internal class implementation
protected final boolean tryRelease(int releases) {
    // The state count is accumulated when the lock is added, so the state count needs to be reduced when the lock is released
    int c = getState() - releases;
    if(Thread.currentThread() ! = getExclusiveOwnerThread())// If the thread currently releasing the lock is not the one currently acquiring the exclusive lock, an exception is thrown
        throw new IllegalMonitorStateException();
    boolean free = false;
    // Always return false if the status value is not 0
    if (c == 0) {
        // For reentrant threads, all locks acquired by the thread are released only when the status value decreases to 0
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
Copy the code

The difference between fair and unfair locks

The difference is that there is an extra step to determine whether the current node is the first in the synchronization queue.

/ / fair lock
protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if(! hasQueuedPredecessors() &&// Check whether the current node has a precursor node, if there is a precursor node return true; Return false if the current node is the head node or the current queue is empty, and the lock can be acquired
            compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true; }}else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}
Copy the code

See gitee for the code that tests fair and unfair locks

The test results

Unfair lock:


Fair lock:

**



It can be seen from the results that the thread that has just released the lock may acquire the lock again, so that other threads can only wait in the synchronization queue. If each time different threads acquire the lock is defined as a switch, then the switch times of the fair lock is obviously more than that of the unfair lock.

Difference from Synchronize

  1. ReentrantLock offers more, more comprehensive functionality and more scalability. Such as: time lock wait, can interrupt lock wait
  2. ReentrantLock also provides conditions that are more detailed and flexible for thread wait and wake up operations
  3. ReentrantLock provides a pollable lock request that attempts to acquire the lock and continues if successful, otherwise it can wait until the next run time, whereas synchronized is less likely to generate deadlocks once the lock request is either successful or blocked
  4. ReentrantLock supports a more flexible synchronization code speed, but with synchronized, it can only be acquired and released in the same synchronized block structure. Lock release for ReentrantLock must be handled in finally, otherwise there may be serious consequences.
  5. ReentrantLock supports interrupt handling and performs better than Synchronzied