February 23, 2024

Beforehand we had a take a look at the lock class and the way it works behind the scenes.
On this submit, we will take a look at LockSupport and its native strategies that are one of many constructing blocks for locks and synchronization courses.

As we will see the methods of lock assist are static and behind the scenes, they’re primarily based on the Unsafe class.
The Unsafe class isn’t supposed for use by your code instantly thus solely trusted code can get hold of cases of it.

If we see the strategies obtainable we will establish the park methodology, the unpark methodology and the get Blocker methodology.

public class LockSupport 
...
 
    public static void park() 
    public static void park(Object blocker) 
    public static void parkNanos(lengthy nanos) 
    public static void parkNanos(Object blocker, lengthy nanos) 
    public static void parkUntil(lengthy deadline) 
    public static void parkUntil(Object blocker, lengthy deadline) 
    public static Object getBlocker(Thread t) 
    public static void unpark(Thread thread) 
...

Every thread that makes use of the category LockSupport is related to a allow.
Once we name to park the thread is disabled for thread scheduling functions. Supplied the allow is out there the allow is consumed and the decision returns instantly.

public ultimate class Unsafe 
...
    public native void unpark(Object thread);
    public native void park(boolean isAbsolute, very long time);
...

We will hint down the allow within the supply code implementation and finally find yourself on the POSIX implementation:

void Parker::park(bool isAbsolute, jlong time) 
 
 
  if (Atomic::xchg(&_counter, 0) > 0) return;
  JavaThread *jt = JavaThread::present();
 
  if (jt->is_interrupted(false)) 
    return;
  
 
  struct timespec absTime;
  if (time < 0 

In a nutshell _the counter is a allow and if the allow is greater than 0 it will likely be consumed and the strategy will return instantly. If an interrupt is pending the strategy ought to return instantly. If Nanos to attend have been provided then the time to attend on the situation is calculated. By utilizing the mutex supplied both the thread will wait without end till is unparked or interrupted or the thread will anticipate the Nanos supplied.

Let’s see an instance of parking a thread:

@Take a look at
void park() throws InterruptedException 
    Thread secondaryThread = new Thread(LockSupport::park);
 
    secondaryThread.begin();
 
    secondaryThread.be part of(2000);
    log.data("Couldn't be part of thread is parked");
 
    assertTrue(secondaryThread.isAlive());
    LockSupport.unpark(secondaryThread);
    secondaryThread.be part of();
 
    assertFalse(secondaryThread.isAlive());
    log.data("Thread was unparked");

We begin the thread and park it. Once we attempt to anticipate the principle thread we use a time restrict, if the time restrict was not set we might wait without end. Finally, the time passes and earlier than retrying to affix the thread we unpark. As anticipated the thread is unparked and finally finishes.

If the allow isn’t obtainable the thread lies dormant and disabled for thread scheduling. Initially, the allow is zero. We will attempt with one thread.

@Take a look at
void unParkAndPark() 
    ultimate Thread mainThread = Thread.currentThread();
    LockSupport.unpark(mainThread);
    LockSupport.park();

By utilizing unpark initially we made a allow obtainable, thus on the park the allow was consumed and we returned instantly.

The opposite aspect that we see within the LockSupport class is the blocker.

The Blocker maps to the parkBlocker object within the Thread implementation of java.

public class Thread implements Runnable 
...
    unstable Object parkBlocker;
...

The blocker represents the synchronization object accountable for this thread parking.
Thus we will notice which object is accountable for the thread being parked, which might help for debugging and monitoring functions.
The unsafe strategies are known as to set the parkBlocker worth to the Thread occasion.

Now that we noticed how LockSupport works behind the scenes we will see the instance supplied by Javadoc the FIFOMutex.

import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.LockSupport;
 
public class FIFOMutex 
 
    non-public ultimate AtomicBoolean locked = new AtomicBoolean(false);
    non-public ultimate Queue<Thread> waiters
            = new ConcurrentLinkedQueue<Thread>();
 
    public void lock() 
        boolean wasInterrupted = false;
        Thread present = Thread.currentThread();
        waiters.add(present);
 
        // Block whereas not first in queue or can not purchase lock
        whereas (waiters.peek() != present 
 
    public void unlock() 
        locked.set(false);
        LockSupport.unpark(waiters.peek());
    
 

All threads will likely be added to the queue. Since this can be a FIFO queue, the primary thread ought to mark the locked variable as true and will be capable of proceed and take away itself from the queue. The remainder of the threads ought to be parked. In case of an interrupt LockSupport will exit thus we should always reassert the interrupt on the thread on exit.
Additionally, listen that the blocker is about because the occasion of the FIFOMutex class.

By utilizing unlock the threads which were in a parked state will resume.

We will additionally examine one other utilization of lock assist. Price limiters can profit from LockSupport.
We will examine the source code of the Refill Price Limiter:

    non-public boolean waitForPermission(ultimate lengthy nanosToWait) 
        waitingThreads.incrementAndGet();
        lengthy deadline = currentNanoTime() + nanosToWait;
        boolean wasInterrupted = false;
        whereas (currentNanoTime() < deadline && !wasInterrupted) 
            lengthy sleepBlockDuration = deadline - currentNanoTime();
            parkNanos(sleepBlockDuration);
            wasInterrupted = Thread.interrupted();
        
        waitingThreads.decrementAndGet();
        if (wasInterrupted) 
            currentThread().interrupt();
        
        return !wasInterrupted;
    

In a Price Limiter, the permits to accumulate are restricted over time. In case of a thread making an attempt to accumulate a allow, there’s an possibility to attend till a allow is out there. On this case, we will park the thread. This fashion our price limiter will forestall the time busy on spinning.

That’s it. Now that we find out about lock assist we will proceed with extra fascinating concurrency ideas.