LockSupport in Java

Versions: Java 8

During reading Thread documentation I found a class that existence I've ignored until now - LockSupport. Some of its methods influence Thread states, so it was quite natural choice for the topic of next posts.

Data Engineering Design Patterns

Looking for a book that defines and solves most common data engineering problems? I wrote one on that topic! You can read it online on the O'Reilly platform, or get a print copy on Amazon.

I also help solve your data engineering problems 👉 contact@waitingforcode.com 📩

As told, this post presents LockSupport. The first part explains its role and the API. The second part shows the use of LockSupport through several test cases.

LockSupport presentation

LockSupport is a class located in java.util.concurrent.locks package. Its neighbours are the classes and interfaces as: Lock, ReentrantLock or ReentrantWriteLock. Thanks to this neighbourhood it can be easily deduced that LockSupports is a kind of lock mechanism. The Javadoc confirms that by defining LockSupport as:

Basic thread blocking primitives for creating locks and other synchronization classes.

More specifically, LockSupport provides an alternative for some of Thread's deprecated methods: suspend() and resume(). It uses a concept of permit and parking to detect if given thread should block or not. Permit is associated to each class using LockSupport and is manipulated through park-like methods as:

LockSupport example

Following examples show the utility of LockSupport:

@Test
public void should_unblock_parked_thread() throws InterruptedException {
  List<Integer> iteratedNumbers = new ArrayList<>();
  Thread thread1 = new Thread(() -> {
    int i = 0;
    // park() blocks thread invoking this method
    LockSupport.park();
    while (true) {
      try {
        Thread.sleep(1_000L);
        iteratedNumbers.add(i);
        i++;
      } catch (InterruptedException e) {
        e.printStackTrace();
        Thread.currentThread().interrupt();
      }
    }
  });
  thread1.start();

  Thread thread2 = new Thread(() -> {
    try {
      Thread.sleep(2_600L);
    } catch (InterruptedException e) {
      e.printStackTrace();
      Thread.currentThread().interrupt();
    }
    // unpark(Thread) releases thread specified
    // in the parameter
    LockSupport.unpark(thread1);
  });
  thread2.start();

  Thread.sleep(5_000L);
  thread1.interrupt();

  assertThat(iteratedNumbers).hasSize(2);
  // Only 2 numbers are expected:
  // * thread1 blocks before starting the iteration
  // * thread2 wakes up after ~3 seconds and releases blocked thread1
  // * from 5 seconds allocated to execution, thread1 has only 2
  //   seconds to execute and since the sleep between iterations 
  //   is 1 second, it should make only 2 iterations
  assertThat(iteratedNumbers).containsOnly(0, 1);
}

@Test
public void should_block_thread_with_deadline() throws InterruptedException {
  List<Integer> iteratedNumbers = new ArrayList<>();
  Thread thread1 = new Thread(() -> {
    int i = 0;
    // park() blocks thread invoking this method
    long lockReleaseTimestamp = System.currentTimeMillis()+2_600L;
    LockSupport.parkUntil(lockReleaseTimestamp);
    while (true) {
      try {
        Thread.sleep(1_000L);
        iteratedNumbers.add(i);
        i++;
      } catch (InterruptedException e) {
        e.printStackTrace();
        Thread.currentThread().interrupt();
      }
    }
  });
  thread1.start();

  Thread.sleep(5_000L);
  thread1.interrupt();

  assertThat(iteratedNumbers).hasSize(2);
  // Only 2 numbers are expected because lock is held during ~3 seconds:
  // * thread1 blocks before starting the iteration during ~3 seconds
  // * from 5 seconds allocated to execution, thread1 has only 2 seconds
  //   of execution time and since the sleep between iterations is 1 second, 
  //   it should make only 2 iterations
  assertThat(iteratedNumbers).containsOnly(0, 1);
}

@Test
public void should_prove_that_blocker_is_not_an_exclusive_lock() throws InterruptedException {
  Object lock = new Object();
  boolean[] blocks = new boolean[2];
  Thread thread1 = new Thread(() -> {
    LockSupport.park(lock);
    blocks[0] = true;
  });
  thread1.start();

  Thread thread2 = new Thread(() -> {
    LockSupport.park(lock);
    blocks[1] = true;
  });
  thread2.start();

  Thread.sleep(2_000L);

  // Both threads stopped with the same blocker object (Object lock)
  // It shows that blocker can't work as an exclusive lock mechanism
  Object blockerThread1 = LockSupport.getBlocker(thread1);
  Object blockerThread2 = LockSupport.getBlocker(thread2);

  assertThat(blockerThread1).isEqualTo(lock);
  assertThat(blockerThread2).isEqualTo(lock);
  assertThat(blocks[0]).isFalse();
  assertThat(blocks[1]).isFalse();
}

@Test
public void should_implement_locking_mechanism_with_blocker() throws InterruptedException {
  Object lock = new Object();
  Thread thread1 = new Thread(() -> {
    LockSupport.parkUntil(lock, System.currentTimeMillis()+3_000L);
  });
  thread1.start();

  long timeBeforeLockAcquire = System.currentTimeMillis();

  // Give some guarantee to thread1 to acquire lock
  Thread.sleep(10L);

  // Try to lock current thread as long as
  // thread1 doesn't release its lock - let's suppose
  // that thread1 is making some job needed by current thread
  while (LockSupport.getBlocker(thread1) != null) {
  }
  LockSupport.parkUntil(lock, System.currentTimeMillis()+1_000L);
  long timeAfterLockRelease = System.currentTimeMillis();
  long duration = timeAfterLockRelease - timeBeforeLockAcquire;

  // Duration should be ~4 seconds because of 3 seconds of lock
  // acquired by thread1 and 1-second blocking of current thread
  assertThat(duration).isEqualTo(4_000L);
}

This post shows LockSupport. It's one of solutions to suspend and resume threads in Java. It represents this concept by the abstraction of parking/unparking which means making a permit available/unavailable (resume/suspend thread). It contains simple parking methods and also time-based. LockSupport makes also possible the use of blocker object which with a some of additional logic can be used as lock.

Consulting

With nearly 16 years of experience, including 8 as data engineer, I offer expert consulting to design and optimize scalable data solutions. As an O’Reilly author, Data+AI Summit speaker, and blogger, I bring cutting-edge insights to modernize infrastructure, build robust pipelines, and drive data-driven decision-making. Let's transform your data challenges into opportunities—reach out to elevate your data engineering game today!

👉 contact@waitingforcode.com
đź”— past projects