LockSupport
An overview of the
LockSupport is a programming utility class designed primarily to block and wake up threads. All of its methods are static, and it can block or wake up at any point.
This blocker is used to record who blocked the thread when it was blocked. This blocker is used by thread monitoring and analysis tools to locate the cause.
The LockSupport class is associated with a license for each thread that uses it, and threads that invoke methods of the LockSupport class by default do not hold licenses.
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
System.out.println("Thread starts executing");
LockSupport.park();
System.out.println("Thread execution terminated");
});
thread.start();
TimeUnit.SECONDS.sleep(3);
System.out.println("Executive unpark");
LockSupport.unpark(thread);
}
Copy the code
And wait/notify the difference
- Wait and notify must acquire a lock object before they can be called, but Park does not need to acquire a lock on an object to lock a thread.
- Notify can only randomly select a thread to wake up, but cannot wake up a specified thread. Unpark can wake up a specified thread.
Important method
These methods are native methods that call the Unsafe class
private static final sun.misc.Unsafe UNSAFE;
public final class Unsafe {
public native void park(boolean isAbsolute, long time);
public native void unpark(Thread jthread);
}
Copy the code
park(Object blocker)
SetBlocker records that the current thread is blocked by the Blocker. When a thread is blocked by calling the Park method without a license, this Blocker is logged to the thread itself. You can use a diagnostic tool to see why a Thread is blocking. The diagnostic tool gets the Blocker object by calling the getBlocker(Thread) method, so locksupport.park (this) is recommended;
If the thread calling the park method has a license associated with LockSupport, the call to locksupport.park () will return immediately, otherwise the calling thread will be blocked and suspended. The blocked Thread that called the park method returns when another Thread calls the unpark(Thread Thread) method and takes the current Thread as an argument. In addition, the blocking thread will return if another thread calls the interrupt() method of the blocking thread, sets the interrupt flag, or is falsely awakened.
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false.0L);
setBlocker(t, null); // Clear the Blocker variable when the thread is activated
}
private static void setBlocker(Thread t, Object arg) {
UNSAFE.putObject(t, parkBlockerOffset, arg);
}
Copy the code
unpark(Thread thread)
If the thread was previously suspended by calling park(), it will be woken up after calling unpark.
If the thread has not called park before, let the thread hold a license and call the park method later, which returns immediately.
public static void unpark(Thread thread) {
if(thread ! =null) UNSAFE.unpark(thread);
}
Copy the code
parkNanos(Object blocker, long nanos)
If no license is obtained, the current thread is blocked for a maximum of nanos nanoseconds, and the return condition increases the timeout return from Park ()
public static void parkNanos(Object blocker, long nanos) {
if (nanos > 0) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, nanos);
setBlocker(t, null); }}Copy the code
parkUntil
Block the current thread until deadline;
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(true, deadline);
setBlocker(t, null);
}
Copy the code
The principle of the author
I’m only going to cover some of the important points, but I’ve left out the non-important ones
An overview of the
Each Java thread has a Parker instance with a Parker class definition:
class Parker {
private:
volatile int _counter; // Record permissions
public:
void park(bool isAbsolute, jlong time);
void unpark(a);
}
Copy the code
LockSupport blocks/wakes up threads by controlling _counter, similar to PV operation of Semaphore mechanism, where Semaphore starts at 0 and is at most 1.
A thread blocking requires a permit, which is at most one. When the park method is called, if there is a credential, it will directly consume the credential and exit normally. But if there is no credential, you must block until the credential is available; Unpark, on the other hand, adds a credential, but there can only be one credential at most.
The value of _counter can only be between 0 and 1: a value of 1 indicates that the class has been called by unpark. More calls do not increase the value of _counter. When the thread calls park(), it does not block and _counter is immediately cleared to zero. When 0, the call to park() is blocked.
- Why is it possible to wake up a thread and then block it? Since unpark got a credential, the subsequent call to park will not block because of credential consumption.
- Why waking up twice and then blocking twice blocks the thread. Because the number of credentials is 1 at most, calling unpark twice in succession has the same effect as calling unpark once, and only one certificate will be added. Calling park twice requires two credentials to be consumed.
park()
- check
_counter
Is greater than zero (beforeCall a unpark), is passedAtomic action sets _counter to 0. The thread does not block and returns. - Check if the thread has an interrupt signal, and if so, clear it and return (without throwing an exception).
- Try to through
pthread_mutex_trylock
The _mutexlockTo achieve thread mutex. - Check whether the park timeout period is set. If the timeout period is set, wait through safe_cond_timedWait. If not, call pthread_cond_wait to block. Both of these functions give up CPU use while blocking wait. Until another thread wakes it up (calling pthread_cond_signal). Safe_cond_timedwait /pthread_cond_wait must have acquired the lock _mutex before execution, released the lock before sleep, and acquired the lock again before being awakened.
- Set _counter to zero.
- Release the lock by pthread_mutex_unlock.
void Parker::park(bool isAbsolute, jlong time) {
if (Atomic::xchg(0, &_counter) > 0) return; // unpark is called
if (Thread::is_interrupted(thread, false)) return; / / interrupt
// lock _mutex
if (Thread::is_interrupted(thread, false) || pthread_mutex_trylock(_mutex) ! =0) {
return;
}
// Wait timeout or block until signal wakes up
if (time == 0) {
status = pthread_cond_wait (&_cond[_cur_index], _mutex);
} else {
status = os::Linux::safe_cond_timedwait (&_cond[_cur_index], _mutex, &absTime);
}
_counter = 0; // Wake up and consume the credentials
status = pthread_mutex_unlock(_mutex); / / unlock
}
Copy the code
unpark()
- First get the lock _mutex.
- Regardless of the previous value, _counter is set to 1, so no matter how many function calls unpark() are made, they are invalid and recorded only once.
- Check to see if the thread is blocked and then call pthread_cond_signal to wake it up.
- Finally, the lock _mutex is released.
void Parker::unpark() {
status = pthread_mutex_lock(_mutex);
s = _counter;
_counter = 1; // set _counter to 1
if (s < 1) status = pthread_cond_signal (&_cond[_cur_index]); // Wake up the thread
pthread_mutex_unlock(_mutex);
Copy the code