What is LockSupport
The LockSupport thread tool class defines many methods to control the current thread, commonly known as lock interrupt
Is the basic thread blocking primitive used to create locks and other synchronization classes.
park () and unpark () in LockSupport are blocking threads and unblocking threads respectively
LockSupport class uses a concept called Permit to block and wake up threads. Each thread has a license. The license has only two values: 1 and zero. The default is zero. Permission can be regarded as a (0,1) Semaphore, but unlike Semaphore, the upper limit of accumulation of permission is 1.
Thread waiting wake-up mechanism
Three ways to make threads wait and wake up
Method 1: use the wait() method in Object to make the thread wait, and use the notify() method in Object to wake up the thread
Object wait notify notifyAll
Method 2: use the await() method of Condition in the JUC package to make the thread wait, and use the signal() method to wake up the thread
Condition await signal signalAll
Method 3: LockSupport class can block the current thread and wake up the specified blocked thread
LockSupport park unpark
The wait and notify methods in the Object class implement thread waiting and wake-up
package com.dongguo.locksupport; /** * @author Dongguo * @date 2021/9/6 0006-15:40 * @description: */ public class LockSupportDemo { public static void main(String[] args) { Object objectLock = new Object(); new Thread(()->{ synchronized (objectLock){ try { System.out.println(Thread.currentThread().getName()+"--block"); objectLock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"--Awakened"); } },"t1").start(); new Thread(()->{ synchronized (objectLock){ objectLock.notify(); System.out.println(Thread.currentThread().getName()+"--Send wake-up notification"); } },"t2").start(); } } Operation results: t1--block t2--Send wake-up notification t1--Awakened
Abnormal one
The wait and notify methods in the Object class can only be used in synchronized code blocks or synchronization methods, and appear in pairs. Otherwise, an exception IllegalMonitorStateException will be reported
package com.dongguo.locksupport; /** * @author Dongguo * @date 2021/9/6 0006-15:40 * @description: */ public class LockSupportDemo { public static void main(String[] args) { Object objectLock = new Object(); new Thread(()->{ // synchronized (objectLock){ try { System.out.println(Thread.currentThread().getName()+"--block"); objectLock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"--Awakened"); // } },"t1").start(); new Thread(()->{ // synchronized (objectLock){ objectLock.notify(); System.out.println(Thread.currentThread().getName()+"--Send wake-up notification"); // } },"t2").start(); } } Operation results t1--block Exception in thread "t1" Exception in thread "t2" java.lang.IllegalMonitorStateException at java.lang.Object.notify(Native Method) at com.dongguo.locksupport.LockSupportDemo.lambda$main$1(LockSupportDemo.java:24) 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.dongguo.locksupport.LockSupportDemo.lambda$main$0(LockSupportDemo.java:15) at java.lang.Thread.run(Thread.java:748)
Anomaly two
wait() must be before notify(),
package com.dongguo.locksupport; import java.util.concurrent.TimeUnit; /** * @author Dongguo * @date 2021/9/6 0006-15:40 * @description: */ public class LockSupportDemo { public static void main(String[] args) { Object objectLock = new Object(); new Thread(()->{ try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (objectLock){ try { System.out.println(Thread.currentThread().getName()+"--block"); objectLock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"--Awakened"); } },"t1").start(); new Thread(()->{ synchronized (objectLock){ objectLock.notify(); System.out.println(Thread.currentThread().getName()+"--Send wake-up notification"); } },"t2").start(); } }
Operation results
t1 blocking waiting to be awakened
The await post signal method in the Condition interface implements thread waiting and wake-up
package com.dongguo.locksupport; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @author Dongguo * @date 2021/9/6 0006-15:40 * @description: */ public class LockSupportDemo { public static void main(String[] args) { Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); new Thread(() -> { lock.lock(); try { System.out.println(Thread.currentThread().getName() + "--block"); condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } System.out.println(Thread.currentThread().getName() + "--Awakened"); }, "t1").start(); new Thread(() -> { lock.lock(); try { condition.signal(); System.out.println(Thread.currentThread().getName() + "--Send wake-up notification"); } finally { lock.unlock(); } }, "t2").start(); } } Operation results t1--block t2--Send wake-up notification t1--Awakened
Abnormal one
Only in lock and unlock can the methods of the thread in condition waiting for await and waking up signal be called correctly
package com.dongguo.locksupport; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @author Dongguo * @date 2021/9/6 0006-15:40 * @description: */ public class LockSupportDemo { public static void main(String[] args) { Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); new Thread(() -> { // lock.lock(); try { System.out.println(Thread.currentThread().getName() + "--block"); condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } finally { // lock.unlock(); } System.out.println(Thread.currentThread().getName() + "--Awakened"); }, "t1").start(); new Thread(() -> { // lock.lock(); try { condition.signal(); System.out.println(Thread.currentThread().getName() + "--Send wake-up notification"); } finally { // lock.unlock(); } }, "t2").start(); } } Operation results t1--block Exception in thread "t1" Exception in thread "t2" java.lang.IllegalMonitorStateException at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.signal(AbstractQueuedSynchronizer.java:1939) at com.dongguo.locksupport.LockSupportDemo.lambda$main$1(LockSupportDemo.java:33) 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.dongguo.locksupport.LockSupportDemo.lambda$main$0(LockSupportDemo.java:22) at java.lang.Thread.run(Thread.java:748)
Anomaly two
The order of await before signal cannot be changed
package com.dongguo.locksupport; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * @author Dongguo * @date 2021/9/6 0006-15:40 * @description: */ public class LockSupportDemo { public static void main(String[] args) { Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); new Thread(() -> { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } lock.lock(); try { System.out.println(Thread.currentThread().getName() + "--block"); condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } System.out.println(Thread.currentThread().getName() + "--Awakened"); }, "t1").start(); new Thread(() -> { lock.lock(); try { condition.signal(); System.out.println(Thread.currentThread().getName() + "--Send wake-up notification"); } finally { lock.unlock(); } }, "t2").start(); } }
Operation results
t1 blocked
Constraints used by Object and Condition
To obtain and hold a lock, a thread must be in a lock block (synchronized or lock) and wait/notify or await/signal appear in pairs
You must wait before waking up before the thread can be awakened
Locking will be blocked
park wait and unpark wake in LockSupport class
Main methods
block
park() /park(Object blocker)
Block current thread / block incoming concrete thread
Generally use park()
When calling LockSupport.park()
park()
/** * For thread scheduling, block the current thread before the license is available. * If the license is available, the license is used, and the call returns immediately; * Otherwise, disable the current thread for thread scheduling and put it into sleep before one of the following three conditions occurs: * 1. Another thread calls unpark with the current thread as the target * 2. Some other thread interrupts the current thread * 3. The call returns illogically (i.e., without reason) */ public static void park() { UNSAFE.park(false, 0L); }
park(Object blocker)
public static void park(Object blocker) { //Get current thread Thread t = Thread.currentThread(); //Record the reason why the current thread is blocked. The bottom layer is unsafe.putObject, which is to store the object setBlocker(t, blocker); //Execute park unsafe.park(false, 0L); //After thread recovery, remove the blocking cause setBlocker(t, null); }
The permission is zero by default, so the current thread will block when the park() method is called at the beginning. When another thread sets the permission of the current thread to 1, the park method will wake up,
It then sets permit to zero again and returns.
awaken
unpark(Thread thread)
Wakes up the specified thread that is blocked
LockSupport.unpark(thread);
/** * If the license for the given thread is not yet available, make it available. * If a thread is blocked on a park, it will unblock it. * Otherwise, ensure that the next call to park will not be blocked. * If the given thread has not been started, there is no guarantee that this operation will have any effect. * @param thread: The thread to perform the unpark operation; A null parameter indicates that this operation has no effect. */ public static void unpark(Thread thread) { if (thread != null) UNSAFE.unpark(thread); }
After calling the unpark(thread) method, the permission permission of the thread will be set to 1 (note that calling the unpark method multiple times will not accumulate, and the permission value is still 1) will automatically wake up the thread, that is, the LockSupport.park() method in the previous blocking will return immediately.
From the source code, we can see that the real implementation is in Unsafe.class
public native void unpark(Object var1); public native void park(boolean var1, long var2);
Compared with wait & notify (await signal of condition) of Object
wait, notify and notifyAll must be used together with Object Monitor, but park and unpark do not
Park & unpark is used to block and wake up threads by thread, while notify can only wake up one waiting thread randomly, notifyAll
It is not so accurate to wake up all waiting threads
Park & unpark can unpark first, while wait & notify cannot notify first
code
package com.dongguo.locksupport; import java.util.concurrent.locks.LockSupport; /** * @author Dongguo * @date 2021/9/6 0006-16:14 * @description: */ public class LockSupportDemo1 { public static void main(String[] args) { Thread t1 = new Thread(() -> { System.out.println(Thread.currentThread().getName() + "--block"); LockSupport.park(); System.out.println(Thread.currentThread().getName() + "--Awakened"); }, "t1"); t1.start(); new Thread(()->{ System.out.println(Thread.currentThread().getName() + "--Send wake-up notification"); LockSupport.unpark(t1); },"t2").start(); } } Operation results t1--block t2--Send wake-up notification t1--Awakened
LockSupport solves thread blocking and wakeup in Object and Condition
To obtain and hold a lock, a thread must solve the problem in the lock block (synchronized or lock)
Moreover, the order of park and unpark can not be fixed
package com.dongguo.locksupport; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport; /** * @author Dongguo * @date 2021/9/6 0006-16:14 * @description: */ public class LockSupportDemo1 { public static void main(String[] args) { Thread t1 = new Thread(() -> { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "--block"); LockSupport.park(); System.out.println(Thread.currentThread().getName() + "--Awakened"); }, "t1"); t1.start(); new Thread(()->{ System.out.println(Thread.currentThread().getName() + "--Send wake-up notification"); LockSupport.unpark(t1); },"t2").start(); } } Operation results t2--Send wake-up notification t1--block t1--Awakened
It is difficult to understand that the unpark operation can be performed before the park operation. That is, the license is provided first. When a thread calls Park and has a license, it consumes the license and can continue to run. This is actually necessary. Consider the simplest producer consumer model: the consumer needs to consume a resource, so it calls the park operation to wait; Producer then produces resources, and then calls unpark to grant Consumer permission. It is very likely that the producer produces first, and the consumer may not be constructed (for example, the thread has not been started or switched to the thread). When the consumer is ready to consume, it is obvious that the resources have been produced and can be used directly. Of course, the park operation can run directly. Without this semantics, it will be very difficult to operate.
However, this "license" cannot be superimposed, and the "license" is one-time.
For example, thread t2 calls the unpark function three times in a row. When thread t1 calls the park function, this "permission" will be used. If thread t1 calls Park again, it will enter the waiting state.
Execute two park() before unpark()
package com.dongguo.locksupport; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport; /** * @author Dongguo * @date 2021/9/6 0006-16:14 * @description: */ public class LockSupportDemo1 { public static void main(String[] args) { Thread t1 = new Thread(() -> { System.out.println(Thread.currentThread().getName() + "--block"); LockSupport.park(); LockSupport.park(); System.out.println(Thread.currentThread().getName() + "--Awakened"); }, "t1"); t1.start(); new Thread(()->{ System.out.println(Thread.currentThread().getName() + "--Send wake-up notification"); LockSupport.unpark(t1); LockSupport.unpark(t1); LockSupport.unpark(t1); },"t2").start(); } }
Operation results
Solve the problem of using two threads and waking up twice
package com.dongguo.locksupport; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport; /** * @author Dongguo * @date 2021/9/6 0006-16:14 * @description: */ public class LockSupportDemo1 { public static void main(String[] args) { Thread t1 = new Thread(() -> { System.out.println(Thread.currentThread().getName() + "--block"); LockSupport.park(); LockSupport.park(); System.out.println(Thread.currentThread().getName() + "--Awakened"); }, "t1"); t1.start(); new Thread(()->{ System.out.println(Thread.currentThread().getName() + "--t2 Send wake-up notification"); LockSupport.unpark(t1); },"t2").start(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ System.out.println(Thread.currentThread().getName() + "--t3 Send wake-up notification"); LockSupport.unpark(t1); },"t3").start(); } } Operation results t1--block t2--t2 Send wake-up notification t3--t3 Send wake-up notification t1--Awakened
Make sure that unpark does not wake up the same park
Execute two unpark() before park()
package com.dongguo.locksupport; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport; /** * @author Dongguo * @date 2021/9/6 0006-16:14 * @description: */ public class LockSupportDemo1 { public static void main(String[] args) { Thread t1 = new Thread(() -> { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "--block"); LockSupport.park(); LockSupport.park(); System.out.println(Thread.currentThread().getName() + "--Awakened"); }, "t1"); t1.start(); new Thread(()->{ System.out.println(Thread.currentThread().getName() + "--Send wake-up notification"); LockSupport.unpark(t1); LockSupport.unpark(t1); },"t2").start(); } }
Operation results
Execute t2 first and unpark twice to set the license to 1
LockSupport.unpark(t1);
LockSupport.unpark(t1);
Execute t1
Execute park LockSupport.park() for the first time; Consumption license
The park permission of the second execution is 0, and blocking is entered
Responsiveness of LockSupport to interruptions
The bottom layer of AQS (AbstractQueuedSynchronizer) is to call the park() method to ensure that after the blocking is awakened, if locking fails, it can block again to reduce resource consumption. When a waiting queue occurs, a node in the queue can respond to the interrupt:
private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); }
If the thread is blocked by calling park, it can respond to the interrupt request (the interrupt status is set to true), but it will not throw an InterruptedException.
package com.dongguo.locksupport; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport; /** * @author Dongguo * @date 2021/9/6 0006-20:49 * @description: */ public class LockSupportDemo2 { public static void main(String[] args) { Thread t1 = new Thread(() -> { System.out.println( "park Front interrupt flag bit 1:" + Thread.currentThread().isInterrupted()); LockSupport.park(); System.out.println(Thread.currentThread().getName()+"Wake up interrupt state 2"+ Thread.currentThread().isInterrupted()); }, "t1"); t1.start(); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } t1.interrupt(); } } Operation results park Front interrupt flag bit 1: false t1 Wake up interrupt state 2 true
The state of the thread of park() after being interrupted() is the same as that of the normal thread after being interrupted()
The park method can respond to interrupts
Therefore, the park method allows the thread to wake up after waiting
1unpark
2interrupt
However, unpark is recommended
If the break flag is already true, the park will be invalidated
After the thread of park() is interrupted()
park() fails again and cannot be blocked
package com.dongguo.locksupport; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport; /** * @author Dongguo * @date 2021/9/6 0006-20:49 * @description: */ public class LockSupportDemo2 { public static void main(String[] args) { Thread t1 = new Thread(() -> { System.out.println( "park Front interrupt flag bit 1:" + Thread.currentThread().isInterrupted()); LockSupport.park(); System.out.println(Thread.currentThread().getName()+"Wake up interrupt state 2"+ Thread.currentThread().isInterrupted()); }, "t1"); t1.start(); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } t1.interrupt(); LockSupport.unpark(t1); } } Operation results park Front interrupt flag bit 1: false t1 Wake up interrupt state 2 true park Front interrupt flag bit 1: true t1 Wake up interrupt state 2 true park Front interrupt flag bit 1: true t1 Wake up interrupt state 2 true park Front interrupt flag bit 1: true t1 Wake up interrupt state 2 true park Front interrupt flag bit 1: true t1 Wake up interrupt state 2 true
The for loop is awakened by interrupt() after the first park block
After that, the loop park cannot block the thread.
You can use Thread.interrupted() to clear the broken state
package com.dongguo.locksupport; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport; /** * @author Dongguo * @date 2021/9/6 0006-20:49 * @description: */ public class LockSupportDemo2 { public static void main(String[] args) { Thread t1 = new Thread(() -> { for (int i = 0; i < 5; i++) { System.out.println( "park Front interrupt flag bit 1:" + Thread.currentThread().isInterrupted()); Thread.interrupted();//Returns the interrupt status and clears the interrupt status LockSupport.park(); System.out.println(Thread.currentThread().getName()+"Wake up interrupt state 2"+ Thread.currentThread().isInterrupted()); } }, "t1"); t1.start(); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } t1.interrupt(); } }
reference resources