PrefaceLockSupport yes JUC A tool class commonly used in , The main function is to suspend and wake the thread . In the reading JUC Source code often see , So it's necessary to understand .
official account :liuzhihangs , Record the skills in work study 、 Development and source notes ; From time to time to share some of the life experience . You are welcome to guide !
Introduce
The basic thread blocking primitive creates locks and other synchronization classes .Basic thread blocking primitives for creating locks and other synchronization classes.LockSupport Class is associated with a permission for each thread that uses it ( In a sense Semaphore class ). If the license is available , call park Will return immediately , And consume it in the process ; Otherwise it may block . If the license is not available , You can call unpark Make the license available .( But with Semaphore Different , Permits can't accumulate . There is at most one .)
Method park and unpark Provides an effective means of blocking and unlocking threads without deadlock problems , and Thread.suspend and Thread.resume It can't be used for this purpose : Because of the license , One thread call park And another thread tries to unpark The competition between them will remain active . Besides , If the caller thread is interrupted ,park Will return , And support setting timeout . The park Method may also return at any other time ,“ There is no reason ”, Therefore, it is usually necessary to re check the condition after a call in a cycle . In that sense park As “ Busy waiting ” Don't waste too much time on spin optimization , But it has to be paired unpark Use .
These three forms of park And support blocker Object parameters . When a thread is blocked, it is allowed to use monitoring and diagnostic tools , To determine the cause of the thread blocking .( Diagnostic tools can be used getBlocker(Thread) Method .) It is also recommended to use with blocker Parametric park Method , The usual way is blocker Set to this .
The meaning of the above can be summed up as follows :
- The license (permit) The upper limit of is 1, That is to say, only 0 or 1 .
- park: Without permission ,permit by 0 , call park It will block ; When you have permission ,permit by 1 , call park A permit will be deducted , Then return .
- unpark: Without permission ,permit by 0 , call unpark Will add a license , Because the license limit is 1 , So many calls are only for 1 individual .
- There is no permission at the beginning of the thread .
- park If the current thread is interrupted , Will return immediately , It doesn't throw an interrupt exception .
- park Method calls are generally placed in a loop judgment body .
It's roughly as shown :
Here is the example in the source code annotation :
/**
* FIFO An exclusive lock
*/
class FIFOMutex {
private final AtomicBoolean locked = new AtomicBoolean(false);
private final Queue<Thread> waiters = new ConcurrentLinkedQueue<Thread>();
public void lock() {
boolean wasInterrupted = false;
Thread current = Thread.currentThread();
waiters.add(current);
// Block while not first in queue or cannot acquire lock
// Not at the head of the queue , Or the lock is occupied , The block , Only the queue header can obtain the lock
while (waiters.peek() != current || !locked.compareAndSet(false, true)) {
LockSupport.park(this);
if (Thread.interrupted()) // ignore interrupts while waiting
wasInterrupted = true;
}
waiters.remove();
if (wasInterrupted) // reassert interrupt status on exit
current.interrupt();
}
public void unlock() {
locked.set(false);
LockSupport.unpark(waiters.peek());
}
}
verification
Does the thread have permission to start ?
public class LockSupportTest {
public static void main(String[] args) {
System.out.println(" Start execution ……");
LockSupport.park();
System.out.println("LockSupport park after ……");
}
}
- After execution, you will find , Code in park Block at . explain , There is no permission for threads to start with .
Add licenses and consume licenses
public class LockSupportTest {
public static void main(String[] args) {
System.out.println(" Start execution ……");
LockSupport.unpark(Thread.currentThread());
System.out.println(" perform - park");
LockSupport.park();
System.out.println("LockSupport park after ……");
}
}
public class LockSupportTest {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(" Threads " + Thread.currentThread().getName() + " Start execution park");
LockSupport.park(this);
System.out.println(" Threads " + Thread.currentThread().getName() + " perform park end ");
}
});
thread.start();
// Guarantee The thread above executes first , And then the main thread
Thread.sleep(5000);
System.out.println(" Start execution unpark(thread)");
LockSupport.unpark(thread);
Thread.sleep(5000);
System.out.println(" perform unpark(thread) end ");
}
}
From the above example, we can see that :
- perform unpark You can give a certificate to the specified thread .
- Thread is currently being park Blocking , At this point, after giving the certificate , park Will consume certificates , And then go ahead and do it .
The license limit is 1
public class LockSupportTest {
public static void main(String[] args) {
System.out.println("unpark 1 Time ");
LockSupport.unpark(Thread.currentThread());
System.out.println("unpark 2 Time ");
LockSupport.unpark(Thread.currentThread());
System.out.println(" perform - park 1 Time ");
LockSupport.park();
System.out.println(" perform - park 2 Time ");
LockSupport.park();
System.out.println("LockSupport park after ……");
}
}
- Thread blocking , It can be seen that permit There can only be one
Interruptions can make park Continuing execution does not throw an exception
public class LockSupportTest {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(" Threads " + Thread.currentThread().getName() + " Start execution park");
LockSupport.park(this);
System.out.println(" Threads " + Thread.currentThread().getName() + " perform park end ");
System.out.println(" Threads " + Thread.currentThread().getName() + " Start execution park The second time ");
LockSupport.park(this);
System.out.println(" Threads " + Thread.currentThread().getName() + " perform park The second time end ");
}
});
try {
thread.start();
// Guarantee The thread above executes first , And then the main thread
Thread.sleep(5000);
System.out.println(" Start execution unpark(thread)");
// LockSupport.unpark(thread);
thread.interrupt();
Thread.sleep(5000);
System.out.println(" perform unpark(thread) end ");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Output results :
/Library/Java/JavaVirtualMachines/jdk1.8.0_221.jdk/Contents/Home/bin/java ...
Threads Thread-0 Start execution park
Start execution unpark(thread)
Threads Thread-0 perform park end
Threads Thread-0 Start execution park The second time
Threads Thread-0 perform park The second time end
perform unpark(thread) end
- You can see that thread breaks ,park Will continue to execute , And there's no exception thrown .
- thread.interrupt(); After call , Set thread interrupt flag ,unpark No clear interrupt flag , the second park Will continue to carry out .
Using diagnostic tools
liuzhihang % > jps
76690 LockSupportTest
77130 Jps
liuzhihang % > jstack 77265
...
"main" #1 prio=5 os_prio=31 tid=0x00007f7f3e80a000 nid=0xe03 waiting on condition [0x000070000dfcd000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:304)
at com.liuzhihang.source.LockSupportTest.main(LockSupportTest.java:14)
- The middle part is omitted , But you can see that the thread enters
WAITING
state
Source code analysis
public class LockSupport {
private static final sun.misc.Unsafe UNSAFE;
/**
* For threads thread Set up a license
* No license , Add a license , With permission , Do not add
* If the thread is because of park Blocked , After adding a license , It will clear the blocking state
*/
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
/**
* With permission , Use the license
* No permission , Blocking threads , Until permission is obtained
* Pass on blocker To facilitate the use of diagnostic tools
*/
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
/**
* Set thread's blocker attribute
*/
private static void setBlocker(Thread t, Object arg) {
// Even though volatile, hotspot doesn't need a write barrier here.
UNSAFE.putObject(t, parkBlockerOffset, arg);
}
}
LockSupport Of park unpark Method , The actual call is the underlying Unsafe Class native Method .
public final class Unsafe {
public native void unpark(Object var1);
public native void park(boolean var1, long var2);
}
Now that we call Unsafe We can't stop here .
hotspot Source code
This is the source code of the download official package , Read and review the information to understand the general logic , What's unclear , Hope to be guided out .
You can also skip and look at the conclusion directly .
see jdk Source code
http://hg.openjdk.java.net/jd...
This is in the form of os_linux Code, for example
http://hg.openjdk.java.net/jd...
- At the bottom, a
_counter
By updating the_counter
To indicate whether there is proof . - stay park when , Judge
_counter
by 0, Then block and wait , by 1 Then get updated to 0 And back to . - stay unpark when , Judge
_counter
by 0, Then give proof , And wake up the thread , by 1 Then return directly .
summary
The conclusion is the same as expected .
- The license (permit) The upper limit of is 1, That is to say, only 0 or 1 .
- park: Without permission ,permit by 0 , call park It will block ; When you have permission ,permit by 1 , call park A permit will be deducted , Then return .
- unpark: Without permission ,permit by 0 , call unpark Will add a license , Because the license limit is 1 , So many calls are only for 1 individual .
- There is no permission at the beginning of the thread .
- park If the current thread is interrupted , Will return immediately , It doesn't throw an interrupt exception .
Expand
- park/unpark and wait/notify difference
- park Block the current thread ,unpark Wake up the specified thread .
- wait() It needs to be used in combination with locks , And release the lock resource , If no timeout is set , You need to notify() Wake up the . and notify() The wake-up thread is random .