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.

New ebook 🔥
Learn 84 ways to solve common data engineering problems with cloud services.
👉 I want my copy
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:
- park() - blocks the execution of the current thread.
- time-based park - a thread can also be "parked" within specified delay: during some time (parkNanos(long)) or until some time (parkUntil(long))
- park with blocker - park methods allow to pass an object called blocker: (park(Object), parkNanos(Object, long), parkUntil(Object, long)). The blocker is assigned to blocked thread. But beware, it's not a kind of exclusive lock. More than that, the blocker is assigned to thread even before stopping it:
public static void park(Object blocker) { Thread t = Thread.currentThread(); setBlocker(t, blocker); UNSAFE.park(false, 0L); setBlocker(t, null); }
Only with some extra work it could be used as for example a lock. It's shown in one of examples from the next part. - unpark(Thread) - unblocks given Thread, i.e. the permit is made available again.
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.