Java-reentrant lock Reentrantlock Implementation principle __java

Source: Internet
Author: User
Tags cas static class
Java-reentrant lock Reentrantlock Implementation principle

At the implementation level, in addition to relying on CAS (compareandset) methods, it is also dependent on some methods in class Locksupport. First, Locksupport

Class Locksupport is located in the package Java.util.concurrent.locks, the basic method of which is

public static void Park () public
static void Parknanos (long Nanos) public
static void Parkuntil (long deadline) 
  public static void Unpark (thread thread)

The park method will allow the waiting to discard the CPU and enter the wait (in the state of the system), which will not be scheduled. The Unpark method is called by other threads, where the Unpark method causes the thread specified by the parameter to recover the running state:

public class Locksupporttest {public
    static void Main (string[] args) throws interruptedexception {
        Thread t = new Thread () {public
            void run () {
                locksupport.park ();//Discard CPU
                System.out.println ("exit");
            }
        ;
        T.start (); Start Thread
        thread.sleep (1000);//Sleep 1 sec to ensure that child threads run first
        System.out.println ("after 1 second");
        Locksupport.unpark (t);
    }

In the above code, first the main thread starts a child thread T, and then the thread T calls the park into the blocking state. The main thread sleeps 1 seconds to ensure that the child threads have called the Locksupport.park () method. The last main thread calls the Unpark method, which causes the child thread T to resume running and print out the output at the console.

The park method differs from the Thread.yield () method. Yield just tells the operating system to let other threads run first, but it can still run. And the park method is to discard the running qualification of the thread, so that the thread enters the waiting wait state.

At the same time, the park method responds to interrupts , and when an interruption occurs, the park method returns and the interrupt state of the thread is reset.

The park method has two variants Parknanos: You can specify the maximum time to wait, and the number of nanoseconds relative to the current time. Parkuntil: You can specify the maximum waiting time, the absolute time of the parameter, and the number of milliseconds relative to Yu Yuan.

When the wait times out, the method returns. At the same time, there are other variants that can specify an object that is waiting for the object to be debugged for debugging, and in general, the parameter passed is this, for example:

public static void Park (Object blocker)

Locksupport contains a method that returns the Blocker object of a thread:

/**
 * Returns the Blocker object supplied to the most recent
 * invocation of A-park method, has not yet Unbloc ked, or null
 * if not blocked.  The value returned is just a momentary
 * snapshot--the thread could have since unblocked or blocked on a
 * differe NT Blocker object.
 *
 @param t the thread
 * @return the blocker
 * @throws nullpointerexception if argument is null
 * @since 1.6
 */public
static Object Getblocker (Thread t) {
    if (t = = null)
        throw new NullPointerException (); C15/>return unsafe.getobjectvolatile (t, parkblockeroffset);
}
Second, Aqs

In Java, in addition to reentrant locks, there are other concurrency tools, such as Reentrantreadwritelock,semaphore,countdownlatch, and so on, their implementation there are other similar places, in order to reuse code, Java provides an abstract class Abstractqueuedsynchronizer, abbreviated AQS, which simplifies the implementation of concurrency tools.

This is a simple introduction to Aqs, encapsulates a state in Aqs, and provides a way to query and set states for subclasses:

private volatile int state;
Protected final int getState ()
protected final void setstate (int newstate)
protected Final Boolean Compareandsetstate (int expect, int update)

When used for the implementation of a lock, Aqs can save the current holding thread of the lock, providing methods for querying and setting:

private transient Thread exclusiveownerthread;
Protected final void Setexclusiveownerthread (thread T)
protected final Thread getexclusiveownerthread ()

Aqs internally maintains a waiting queue, and the CAs method is used to implement the non-blocking algorithm for updating. Third, Reentrantlock

Reentrantlock internal use of AQS, there are mainly the following three internal classes.

Abstract static class Sync extends Abstractqueuedsynchronizer
static final class Nonfairsync extends Sync
static Final class Fairsync extends Sync

Where sync is an abstract class, Nonfairsync is the class that is used when fair is false, and Fairsync is the class that needs to be used when fair is true.

Within the Reentrantlock there is a SYSC member:

Private final sync sync;

The member is initialized in the constructor method, for example:

/**
 * Creates an instance of {@code Reentrantlock}.
 * This is equivalent to using {@code Reentrantlock (False)}.
 */Public
Reentrantlock () {
    sync = new Nonfairsync ();
}

Next look at the implementation of the basic method Lock/unlock in Reentrantlock. 3.1 Lock

Because sync defaults to Nonfairsync, it's more often than not fair. Where the Nonfairsync lock code is:

/**
 * performs lock.  Try immediate barge, backing up to normal
 * acquire on failure.
 */
final void Lock () {
    if (compareandsetstate (0, 1))
        Setexclusiveownerthread (Thread.CurrentThread ());
    else
        acquire (1);
}

Reentrantlock use state to indicate the number of locks and holds, if not currently locked, obtain a lock immediately, otherwise call acquire (1); method to obtain the lock, where acquire is the method in Aqs, which is now:

/**
 * Acquires in exclusive mode, ignoring interrupts.  Implemented
 * By invoking in least once {@link #tryAcquire},
 * Returning on success.  Otherwise the thread is queued, possibly
 * repeatedly blocking and unblocking, invoking {@link
 * #tryAcquire} unti L success.  This is the can used
 * To implement method {@link Lock#lock}.
 *
 * @param arg the acquire argument.  This are conveyed to
 *        {@link #tryAcquire} but be otherwise uninterpreted and
 *        can represent any Thing.
 */public
final void acquire (int arg) {
    if (!tryacquire (ARG) &&
        acquirequeued (Addwaiter ( node.exclusive), Arg)
        selfinterrupt ();
}

This code calls the Tryacquire method to attempt to acquire the lock, which requires a quilt class rewrite, and the implementation in Nonfairsync is:

Protected Final Boolean tryacquire (int acquires) {return
    nonfairtryacquire (acquires);
}

The Nonfairtryacquire method is implemented by the abstract class Sync:

/**
 * 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;
}

This code means that if it is not locked, the CAS is used for locking, and if the current thread is locked, the number of locks is increased.

If the Tryarquire method returns false, the Acquire method continues to invoke Acquirequeued (Addwaiter (node.exclusive), Arg).

Where Addwaiter creates a new node, represents the current thread, and then joins the internal wait queue.

This section can refer to Bowen: https://blog.csdn.net/yanyan19880509/article/details/52345422

After you wait for the queue, call acquirequeued to try to acquire the lock with the following code:

/** * acquires in exclusive uninterruptible mode for thread already in * queue.
 Used by the condition wait methods as as as the acquire. * @param node the node * @param arg the acquire argument * @return {@code true} if interrupted while waiting * * FINA
    L Boolean acquirequeued (Final node node, int arg) {Boolean failed = true;
        try {Boolean interrupted = false; for (;;)
            {final Node P = node.predecessor ();
                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); }
}

Here the code body is a dead loop, in each loop, first check whether the current node is the first waiting node, if it is and can acquire a lock, the current node from the waiting queue to remove the ice clean return, otherwise through the Parkandcheckinterrupt method to eventually call Locksupport.park and discard the CPU, enter the wait state, after being awakened to check if there has been an interruption, record the interrupt flag. and return the interrupt flag.

If an interrupt occurs, the acquire method calls the Selfinterrupt method to set the interrupt flag bit, whose implementation code is:

/**
 * Convenience to interrupt the current thread.
 */
static void Selfinterrupt () {
    thread.currentthread (). interrupt ();
}

The basic process by which the lock method can be drawn is: if the lock is obtained, it is obtained immediately and the waiting queue is not added. After being awakened, check to see if you are the first waiting thread, and return if you can get a lock, or continue waiting. If an interrupt occurs during this process, lock records the interrupt flag bit, but does not return early or throws an exception 3.2 unlock

The Reentrantlock Unlock method code is:

/**
 * Attempts to release this lock.
 * <p>if The current thread is the holder of this
 lock then the hold
 * count is decremented.  If the hold count is now zero then the lock
 * is released.  If the current thread isn't the holder of this
 * lock then {@link Illegalmonitorstateexception} is thrown.
 *
 * @throws illegalmonitorstateexception If the current thread does not
 *         hold this lock
* * public void Unlock () {
    sync.release (1);
}

The release method that is invoked is the method defined in Aqs, which is actually:

/**
 * releases in exclusive mode.  Implemented by unblocking one or
 * More threads if {@link #tryRelease} returns True.
 * This can is used to implement method {@link Lock#unlock}.
 *
 * @param arg the release argument.  This value is conveyed to
 *        {@link #tryRelease} but is otherwise uninterpreted and
 *        can represent Anythi Ng you like.
 * @return The value returned from {@link #tryRelease}
/Public Final Boolean release (int arg) {
    if (Tryrel Ease (ARG)) {
        Node h = head;
        if (h!= null && h.waitstatus!= 0)
            unparksuccessor (h);
        return true;
    }
    return false;
}

The Tryrelease method modifies the thread state and releases the lock, and the Unparksuccessor method invokes the Locksupport.unpark to wake the first waiting thread. In fact, the present is:

/** * wakes up node ' s successor, if one exists. * @param node the node/private void Unparksuccessor (node node) {/* * If status is negative (i.e., Possibl  Y needing signal) try * to clear in anticipation of signalling.
     It is OK if this * fails or if the status is changed by waiting thread.
    */int ws = Node.waitstatus;

    if (WS < 0) compareandsetwaitstatus (node, WS, 0);  * * Thread to Unpark are held in successor, which is normally * just the next node. But if cancelled or apparently null, * traverse backwards from tail to find the actual * non-cancelled successor
     .
    */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
} if (s!= null) Locksupport.unpark (s.thread); }
3.3 Fair and unfair comparisons

The main difference between Fairsync and Nonfairsync is: When acquiring a lock, in the Tryacquire method, if the current thread state is not locked, then c = = 0,FAIRSYSC will have one more check, implemented as follows:

Protected Final Boolean tryacquire (int acquires) {
        final Thread current = Thread.CurrentThread ();
        int c = getState ();
        if (c = = 0) {
            if (!hasqueuedpredecessors () &&
                compareandsetstate (0, acquires)) {
                Setexclusiveownerthread (current);
                return true;
            }
        ...
}

The purpose of this check is to acquire a lock when there are no other threads that wait longer.

why not default to guarantee fairness. to ensure fair overall performance is lower, not because of the slow check, but because the active thread will not be able to get the lock, thus entering the waiting state, causing frequent context switching, reducing overall efficiency.

In general, the order in which threads run does not affect very much. When the program runs for a long time, in the statistical agitation, the unfair threading approach is basically fair and can make the active thread run continuously.

It should be noted that even if the constructed parameter fair is true, the Trylock method without parameters in Reentrantlock is not guaranteed to be fair, and it does not detect whether there are other threads that wait longer.

public Boolean Trylock () {return
    sync.nonfairtryacquire (1);
}

public boolean Trylock (long timeout, timeunit unit)
            throws Interruptedexception {return
    Sync.tryacquirenanos ( 1, Unit.tonanos (timeout));

This can be seen clearly from the method call. Iv. Comparison of Reentrantlock and synchronized

Compared to synchronized, Reentrantlock can achieve the same semantics as synchronized, and it supports the acquisition of locks in non-blocking mode, and can also be used to interrupt, limit congestion, and be more flexible. But the use of synchronized is simpler and the amount of code is less.

Synchronized represents a declarative programming mindset that programmers express more as a kind of synchronous declaration. The Java system is responsible for implementation, the programmer is not aware of implementation details, and explicit locks represent an imperative programming thinking, users need to implement all the details.

The benefits of declarative programming, in addition to simplicity, are also reflected in performance. On newer versions of the JVM, the performance of Reentrantlock and synchronized is close, and Java compilers and virtual opportunities are optimized for synchronized implementations, such as automated analysis of synchronized usage, Calls to acquire a lock/release lock are automatically ignored for scenarios where there is no lock competition.

Finally, can use the synchronized to use the synchronized, does not satisfy the use request when considers uses the Reentrantlock.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.