1 introduction

1.1 an overview of the

LockSupport is a blocking primitive for creating locks and other synchronization classes. Here’s how the JDK describes LockSupport. Basic thread blocking primitives for creating locks and other synchronization classes.

1.2 license

LockSupport supports suspending and waking up suspended threads through permit. It can be understood according to the following logic:

  • Pack: if permit exists, the thread will not be suspended and will return immediately; If the permit of the thread does not exist, it is considered that the thread lacks permit and therefore needs to suspend and wait for permit.
  • Unpacked: If the thread’s permit does not exist, release a permit. Because of permit, if the thread is suspended, it will be woken up by the thread scheduler. If the thread permit exists, the permit does not accumulate, as if nothing had been done. Note that this is different from Semaphore.

2 Source code Analysis

2.1 park

Main function: If the license exists, use it and return it immediately. If permission does not exist, suspend the current thread until one of the following things happens:

  • Other threads call the unpark(thread) method, taking the current thread as an argument
  • Other threads interrupt the current thread with Thread#interrupt.
  • The Pack method returns without a reason (the condition that caused the thread to pause should be rechecked when called). This will be explained later.
Public static void park(Object blocker) {public static void park(Object blocker) {t = thread.currentThread (); // Set thread blocker object setBlocker(t, blocker); // Suspend the thread UNSAFE. Park (false, 0L); // Once the thread is woken up, you need to clean up the Blocker. setBlocker(t, null); }Copy the code

2.2 Multiple versions of pack

There are several versions of Pack in LockSupport, as shown below:

methods describe
park(Object) Suspend the current thread, as described in the source analysis for Pack above
parkNanos(Object, long) Specifies a hang time (relative to the current time), when the time is up to automatically wake up; For example, it will wake up after 1000 nanoseconds
parkUntil(Object, long) Specify a suspension time (absolute time), when the time is up to automatically wake up; For example, 2018-02-12 is automatically woken up at 21:00.
park() Compared to Park (Object), there is no action to set the Blocker for the thread before suspending it and clean up the Blocker after waking up.
parkNanos(long) Similar to parkNanos(Object, Long), only blocker-related actions are missing
parkUntil(long) Similar to parkUntil(Object, Long), only blocker-related actions are missing

As you can see from the table above, Park supports Blocker objects as parameters. This Blocker object is logged when a thread is blocked so that monitoring tools and diagnostic tools can determine why the thread is blocked. It is recommended to use these blocker versions of methods rather than methods without blocker parameters.

2.3 unpark

Set thread permissions to available.

  • If the thread is currently suspended by Pack, it will be woken up.
  • If the thread is not currently suspended, the next call to Pack will not suspend the thread.
public static void unpark(Thread thread) { if (thread ! = null) UNSAFE.unpark(thread); }Copy the code

3 analysis

3.1 Suspension and Blocking

The main difference between suspends and blocks should be the object to which they are oriented. For threads, LockSupport’s park/unpark is more consistent with the semantics of blocking and wake up. They take “thread” as an argument to the method, which makes the semantics clearer and easier to use. It is difficult to control exactly which thread blocks/wakes up and when. Therefore, it is necessary to wake up either a notify thread or notifyAll threads at random. The park and unpark methods do not have the deadlock problem of Thread.suspend and thread.resume. This is because of permission, there will be no competition between the thread calling park and another thread trying to unpark it. In addition, park returns if the thread is interrupted or times out. The Park method can also return “for no reason” at any other time, so it usually has to be called in a loop where the return condition is rechecked. In this sense, Park is an optimization of “busy waiting”, it does not waste so much time spinning, but it must be paired with unpark to be more efficient.

3.2 Using the Model

The following pseudocode is a common model for Pack.

while ( ! Enable ()) {// Other business logic...... LockSupport.park(this); // Other business logic...... }Copy the code

Here is the code to use Pack in ReentrantLock

final 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);
    }
}

private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);
    return Thread.interrupted();
}
Copy the code