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