[JDK] JDK source code analysis -ReentrantLock

Outline

 

In JDK 1.5 before the lock can only be achieved with the synchronized keyword; 1.5 began offering a ReentrantLock, it is API-level locks. Look at the class signature ReentrantLock and how to use:

public class ReentrantLock implements Lock, java.io.Serializable {}

Typical usage:

public void m() {
  lock.lock();  // block until condition holds
  try {
    // ... method body
  } finally {
    lock.unlock()
  }
}

The use of synchronized keyword usage and the effect is the same. Now that you have synchronized, why is there Lock it? Compared to synchronized, in fact, appear ReentrantLock is not repeated, it adds a lot of functionality, the following briefly introduce some concepts.

 

Lock & Lock fair unfair: the so-called lock it fair, simple to understand whether a series of thread lock to get the order to follow the "first come first served." That is, if the first application of a thread lock to get into the lock, the lock is fair; otherwise, it is unfair lock. ReentrantLock default implementation is unfair and synchronized lock.

 

Reentrant lock: Lock whether reentrant, is whether a thread can acquire the same lock many times, and if so, is reentrant lock. ReentrantLock are synchronized and reentrant lock.

 

Code Analysis

 

Constructor

 

ReentrantLock have two constructors, are as follows:

Private  Final Sync Sync; 

// configured ReentrantLock example of a (non-locking fair) 
public ReentrantLock () { 
    Sync = new new NonfairSync (); 
} 

// Construct a ReentrantLock instance (specified fairness) 
public ReentrantLock ( Boolean Fair) { 
    Sync = Fair ? new new FairSync (): new new NonfairSync (); 
}

You can see, we are two constructor to initialize a member variable of type Sync. Moreover, when the fair value boolean to true, initializing the sync is FairSync, is initialized to false NonFairSync, both represent "fair locks" and "unfair lock." We can see a non-default constructor without parameters fair locks.

 

Common method

 

ReentrantLock common method is several methods Lock interface definitions, as follows:

// Get lock (blocking) 
public  void Lock () { 
    sync.lock (); 
} 

// Get lock (in response to an interrupt) 
public  void lockInterruptibly in () throws InterruptedException { 
    sync.acquireInterruptibly ( . 1 ); 
} 

// try to acquire locks 
public  Boolean tryLock () {
     return sync.nonfairTryAcquire (. 1 ); 
} 

// try to acquire the lock (with a time-out period) 
public  Boolean tryLock ( Long timeout, TimeUnit Unit)
         throws InterruptedException {
     returnsync.tryAcquireNanos (. 1 , unit.toNanos (timeout)); 
} 

// release lock 
public  void UNLOCK () { 
    sync.release ( . 1 ); 
}

It can be seen that the interior is achieved by several methods Sync method call class (or subclass), thus starting the analysis start Sync class code as follows (partially omitted):

// abstract class that inherits the AQS 
abstract  static  class Sync the extends AbstractQueuedSynchronizer { 

    // The method to acquire the lock, implemented by subclasses 
    abstract  void Lock (); 

    // tryLock unfair method to achieve lock 
    Final  Boolean nonfairTryAcquire ( int Acquires) {
         Final current = the thread Thread.currentThread ();
         // Get the AQS state variable 
        int C = getState ();
         // if 0, indicates that the current is not being used by another thread 
        IF (C == 0 ) {
             // CAS modified state, If the amendment is successful, indicating successful access to resources 
            if(compareAndSetState (0 , Acquires)) {
                 // the owner setting the current thread, the thread means the current resources are successfully acquired 
                setExclusiveOwnerThread (Current);
                 return  to true ; 
            } 
        } 
        // State is not 0, and the owner is the current thread
         // It indicates the current thread has acquired the resource, represented here "reentry" 
        the else  IF (current == getExclusiveOwnerThread ()) {
             int NEXTC = C + acquires;
             IF (NEXTC <0) // overflow 
                the throw  new new Error ( "COUNT Lock the Maximum exceeded Number " );
             //Modify the state value (because the current thread has access to resources, there is no competition, so no CAS operation) 
            setState (NEXTC);
             return  to true ; 
        } 
        return  false ; 
    } 

    // release the lock operation (on the state to do subtraction) 
    protected  Final  boolean tryRelease ( int Releases) {
         int C = getState () - Releases;
         IF (Thread.currentThread ()! = getExclusiveOwnerThread ())
             the throw  new new IllegalMonitorStateException ();
         Boolean Free = to false ;
         IF (C == 0  ) {
            Free= To true ;
             // success after the release owner to empty 
            setExclusiveOwnerThread ( null ); 
        } 
        // change the value of the state
         // PS: because there may be after "re-entry" and therefore a release operation of the current thread is still possible footprint,
         // it will not directly to state 0 
        the setState (C);
         return Free; 
    } 
    
    // other methods ... 
    
    Final  Boolean isLocked () {
         return getState () = 0! ; 
    } 
}

Sync class inherits from AQS, implement a method wherein a non-fair nonfairTryAcquire lock tryAcquire method.

 

As seen from the above code, the lock acquisition and release is variable by modifying the state of the AQS achieved. lock method can be seen as "addition" operation performed on the state, and can be seen as performed unlock "subtraction" operation state, when the state is 0, it indicates that no thread footprint.

 

Lock & Lock fair unfair

 

(1) non-arm lock NonFairSync:

static  Final  class NonfairSync the extends Sync { 
    
    Final  void Lock () {
         // CAS attempt to modify the state value is 1 
        IF (compareAndSetState (0, 1 ))
             // If the modification is successful, then the current thread to owner, acquiring a lock for success 
            setExclusiveOwnerThread (Thread.currentThread ());
         // If the acquisition has failed, the execution of AQS acquire method (exclusive mode access to resources) 
        the else 
            acquire ( . 1 ); 
    } 
    
    protected  Final  Boolean to tryAcquire ( int acquires) {
         return nonfairTryAcquire (acquires); 
    } 
}

It can be seen unfair lock lock operation is: first try to modify the value of the state of CAS, if the modification is successful, the lock is acquired successfully, the owner set to the current thread; otherwise acquire execution methods in AQS, specifically refer to previously " JDK source code analysis -AbstractQueuedSynchronizer (2) " will not be repeated here.

 

(2) Fair lock FairSync:

static  Final  class FairSync the extends Sync { 

    Final  void Lock () { 
        Acquire ( . 1 ); 
    } 
    
    // fair lock tryAcquire achieve 
    protected  Final  Boolean tryAcquire ( int Acquires) {
         Final the Thread Current = Thread.currentThread ();
         int C = getState ( );
         // state of 0 indicates the resource is not occupied 
        IF (c == 0 ) {
             // if there are other threads in the queue waiting, it returns false, indicating acquisition failure;
             //    otherwise, try to modify the state value
             //PS: Here is where the difference between fair and unfair lock lock 
            IF (hasQueuedPredecessors () &&! 
                CompareAndSetState ( 0 {, Acquires)) 
                setExclusiveOwnerThread (Current); 
                return  to true ; 
            } 
        } 
        // if the current thread has occupied the lock, " reentrant " 
        the else  IF (Current == getExclusiveOwnerThread ()) {
             int NEXTC = C + Acquires;
             IF (NEXTC <0 )
                 the throw  new new Error (" COUNT Lock the Maximum exceeded Number " ); 
            the setState (NEXTC); 
            return  to true ;
        }
        return false;
    }
}

It can be seen compared with non-equity locks except lock fair judgment condition that increases the hasQueuedPredecessors, that is, first of all to determine whether there are other threads waiting for the main queue when no other threads to go get in the queue, otherwise acquire failure.

 

In AQS hasQueuedPredecessors implemented as follows:

/**
 * Queries whether any threads have been waiting to acquire longer
 * than the current thread.
 */
public final boolean hasQueuedPredecessors() {
    // The correctness of this depends on head being initialized
    // before tail and on head.next being accurate if the current
    // thread is first in queue.
    Node t = tail; // Read fields in reverse initialization order
    Node h = head;
    Node s;
    return h != t &&
        ((s = h.next) == null || s.thread != Thread.currentThread());
}

 

summary

 

synchronized with ReentrantLock comparison:

The same point: both are mutex reentrant default lock is unfair.

Different point: synchronized grammar level is achieved, automatically acquire and release locks; ReentrantLock level API is implemented manually obtain and release locks.

 

ReentrantLock compared to synchronized advantages:

1. interrupt response;

2. Obtain a lock timeout can be set;

3. Can a fair lock;

4. Binding a plurality of conditions (Condition).

 

After JDK 1.6, synchronized with ReentrantLock performance was essentially flat, JVM future performance optimization will be more inclined to synchronized native. So, how to choose but also according to the actual needs, performance is no longer a reason not to select the synchronized a.

 

Related Reading:

JDK source code analysis -Lock & Condition

JDK source code analysis -AbstractQueuedSynchronizer (2)

 

 

Stay hungry, stay foolish.

PS: This article first appeared in the public micro-channel number] [WriteOnRead.

Guess you like

Origin www.cnblogs.com/jaxer/p/11311997.html