park() and unpark() functions of LockSupport class and case analysis

Official interpretation:

  • LockSupport is the basic thread blocking primitive used to create locks and other synchronization classes;

  • LockSupport class uses a concept called permission to block and wake up threads. Each thread has a permission;

  • permit has only two values: 1 and zero. The default value is zero;

  • You can think of a license as a (0,1) Semaphore, but unlike Semaphore, the cumulative upper limit of a license is 1.

In fact, it is an improved and enhanced version of the thread waiting wake-up mechanism;

park() and unpark() in LockSupport are used to block threads and unblock threads respectively;

We know that the Object class has wait() and notify() methods to wait for the wake-up thread, and the condition interface class in the juc package also has await() and signal() methods to wait for the wake-up thread. Why do we need to use LockSupport's park() and unpark() methods to wait for the wake-up thread?

Let's take a look at the limitations of the thread waiting wake-up method used by the Object class and the condition interface class in the juc package;

1. Test cases

1.1 wait and notify methods

public class LockSupportDemo1 {

    // Lock object
    static Object objectLock = new Object();

    public static void main(String[] args) {

        // The first thread calls the wait() method
        new Thread(()->{
            /*try {
                TimeUnit.SECONDS.sleep(1L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }*/
            synchronized (objectLock){
                System.out.println(Thread.currentThread().getName() + ": Enter and wait");
                try {
                    objectLock.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ": Awakened");
            }
        },"A").start();

        // The second thread calls the notify() method to wake up thread A
        new Thread(()->{
            synchronized (objectLock){
                objectLock.notify();
                System.out.println(Thread.currentThread().getName() + ": notice");
            }
        },"B").start();
    }
}

Normal return:

A: Enter and wait
B: notice
A: Awakened
  • Exception 1: if the synchronization code block method of two threads is removed [lines 10, 18, 23, 26], an exception is thrown:

    A: get into
    Exception in thread "A" Exception in thread "B" java.lang.IllegalMonitorStateException
    	at java.lang.Object.notify(Native Method)
    	at com.beef.LockSupportDemo.lambda$main$1(LockSupportDemo.java:30)
    	at java.lang.Thread.run(Thread.java:748)
    java.lang.IllegalMonitorStateException
    	at java.lang.Object.wait(Native Method)
    	at java.lang.Object.wait(Object.java:502)
    	at com.beef.LockSupportDemo.lambda$main$0(LockSupportDemo.java:19)
    	at java.lang.Thread.run(Thread.java:748)
    

    Therefore, the wait, notify and notifyAll methods in the Object class and the methods used for thread waiting and wake-up must be executed within synchronized (the keyword synchronized must be used).

  • Exception 2: when notify() runs in front of wait() [release 10 ~ 14 lines of code], the thread cannot be awakened and the program cannot execute normally;

    We all know that there is no need to test this. It is put here to highlight that park() and unpark() in LockSupport can do this;

1.2 await and signal methods

    public static void main(String[] args) {

        // The first thread calls the await() method
        new Thread(()->{
            /*try {
                TimeUnit.SECONDS.sleep(1L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }*/
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + ": Enter and wait");
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + ": Awakened");
            }finally {
            }
            lock.unlock();
        },"A").start();

        // The second thread calls the signal() method to wake up thread A
        new Thread(()->{
            lock.lock();
            try {
                condition.signal();
                System.out.println(Thread.currentThread().getName() + ": notice");
            }finally {
                lock.unlock();
            }
        },"B").start();
    }
}

Normal return:

A: Enter and wait
B: notice
A: Awakened
  • Exception 1: if the method of adding and releasing locks of two threads is removed [lines 10, 20, 26, 31], an exception is thrown:

    A: get into
    Exception in thread "A" Exception in thread "B" java.lang.IllegalMonitorStateException
    	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.signal(AbstractQueuedSynchronizer.java:1939)
    	at com.beef.lockSupport.LockSupportDemo2.lambda$main$1(LockSupportDemo2.java:38)
    	at java.lang.Thread.run(Thread.java:748)
    java.lang.IllegalMonitorStateException
    	at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151)
    	at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261)
    	at java.util.concurrent.locks.AbstractQueuedSynchronizer.fullyRelease(AbstractQueuedSynchronizer.java:1723)
    	at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2036)
    	at com.beef.lockSupport.LockSupportDemo2.lambda$main$0(LockSupportDemo2.java:24)
    	at java.lang.Thread.run(Thread.java:748)
    

    Therefore, the await() and signal() methods used for thread waiting and wake-up must be in lock() and unlock();

  • Exception 2: when await() runs in front of signal() [open lines 5 ~ 9 of code], the thread cannot be awakened and the program cannot execute normally;

Summary

Traditional synchronized and Lock implement the constraint of waiting for wake-up notification:

  • To obtain and hold a lock, a thread must be in a lock block (synchronized or lock);
  • You must wait before waking up before the thread can be awakened;

1.3 methods of park and unpark

public class LockSupportDemo3 {

    static Lock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();

    public static void main(String[] args) {

        // The first thread calls the park() method
        Thread a = new Thread(() -> {
            /*try {
                TimeUnit.SECONDS.sleep(1L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }*/
            System.out.println(Thread.currentThread().getName() + ": Enter and lock");
            // Blocking, waiting for wake-up
            LockSupport.park();
            System.out.println(Thread.currentThread().getName() + ": awaken");
        }, "A");
        a.start();

        // The second thread calls the unpark() method to wake up thread A
        new Thread(()->{
            LockSupport.unpark(a);
            System.out.println(Thread.currentThread().getName()+": Unlock");
        },"B").start();
    }
}

Normal return:

A: get into
B: notice
A: Awakened
  • When unpark() runs in front of park() [open lines 10-14], the program can be executed normally;

    return:

    B: Unlock
    A: Enter and lock
    A: awaken
    

    This is equivalent to locksupport in line 12 park(); Was annotated out;

LockSupport summary

park() and unpark() in LockSupport have no lock block requirements and can wake up first and then wait;

2. LockSupport source code

LockSupport is a thread blocking tool class. All methods are static methods. Threads can be blocked at any position. After blocking, there are corresponding wake-up methods. The native code in Unsafe called by LockSupport is controlled through the C language primitive level;

For example, let's look at the park() method:

public static void park() {
    UNSAFE.park(false, 0L);
}

// Click in unsafe Park method
public native void park(boolean var1, long var2);

Keywords: Java Multithreading

Added by elecktricity on Tue, 01 Mar 2022 14:19:31 +0200