1.中斷後LockSupport.park()直接返回
public class MultInterruptParkDemo {
public static volatile boolean flag = true;
public static void main(String[] args) {
ThreadDemo04 t4 = new ThreadDemo04();
t4.start();
t4.interrupt();
flag = false;
}
public static class ThreadDemo04 extends Thread {
@Override
public void run() {
while (flag) {
}
LockSupport.park();
System.out.println("本打印出現在第一個park()之後");
LockSupport.park();
System.out.println("本打印出現在第二個park()之後");
}
}
}
結果:
本打印出現在第一個park()之後
本打印出現在第二個park()之後
1.1 分析
Thread中斷方法分析(包括HotSpot源碼)中interrupt源碼:
void os::interrupt(Thread* thread) {
assert(Thread::current() == thread || Threads_lock->owned_by_self(),
"possibility of dangling Thread pointer");
OSThread* osthread = thread->osthread();
if (!osthread->interrupted()) {
osthread->set_interrupted(true);
如果線程未設置中斷標誌位,會進行設置。
LockSupport及HotSpot層Parker::park/unpark分析中park源碼:
void Parker::park(bool isAbsolute, jlong time) {
// Ideally we'd do something useful while spinning, such
// as calling unpackTime().
// Optional fast-path check:
// Return immediately if a permit is available.
// We depend on Atomic::xchg() having full barrier semantics
// since we are doing a lock-free update to _counter.
if (Atomic::xchg(0, &_counter) > 0) return;
Thread* thread = Thread::current();
assert(thread->is_Java_thread(), "Must be JavaThread");
JavaThread *jt = (JavaThread *)thread;
// Optional optimization -- avoid state transitions if there's an interrupt pending.
// Check interrupt before trying to wait
if (Thread::is_interrupted(thread, false)) {
return;
}
只要線程設置了中斷標誌位,就直接返回,且這裏的Thread::is_interrupted(thread, false)因爲是false,所以不會清除中斷標誌位。
1.2 總結
- t4.interrupt()會設置線程的中斷標誌位
- LockSupport.park()會檢查線程是否設置了中斷標誌位,如果設置了,則返回(這裏並不會清除中斷標誌位)
2.unpark不會累積許可(最多爲1)
public class MultInterruptParkDemo2 {
public static volatile boolean flag = true;
public static void main(String[] args) {
ThreadDemo04 t4 = new ThreadDemo04();
t4.start();
LockSupport.unpark(t4);
LockSupport.unpark(t4);
LockSupport.unpark(t4);
flag = false;
}
public static class ThreadDemo04 extends Thread {
@Override
public void run() {
while (flag) {
}
LockSupport.park();
System.out.println("本打印出現在第一個park()之後");
LockSupport.park();
System.out.println("本打印出現在第二個park()之後");
}
}
}
結果:
本打印出現在第一個park()之後
2.1 分析
LockSupport及HotSpot層Parker::park/unpark分析中park源碼:
void Parker::park(bool isAbsolute, jlong time) {
// Ideally we'd do something useful while spinning, such
// as calling unpackTime().
// Optional fast-path check:
// Return immediately if a permit is available.
// We depend on Atomic::xchg() having full barrier semantics
// since we are doing a lock-free update to _counter.
if (Atomic::xchg(0, &_counter) > 0) return;
每次調用park都會將_counter直接置爲0。
void Parker::unpark() {
int s, status ;
status = pthread_mutex_lock(_mutex);
assert (status == 0, "invariant") ;
s = _counter;
_counter = 1;
每次調用unpark都會將_counter直接置爲1.
2.2 總結
- 因此,總的許可數總是保持在1,無論調用多少次unpark,都只會將_counter置爲1。
- 每次park都會將_counter置爲0,如果之前爲1,則直接返回。後面的park調用就會阻塞。
3.Thread.interrupted()會清除中斷標誌
public class MultInterruptParkDemo3 {
public static volatile boolean flag = true;
public static void main(String[] args) {
ThreadDemo04 t4 = new ThreadDemo04();
t4.start();
t4.interrupt();
flag = false;
}
public static class ThreadDemo04 extends Thread {
@Override
public void run() {
while (flag) {
}
LockSupport.park();
System.out.println("本打印出現在第一個park()之後");
System.out.println(Thread.interrupted());
System.out.println(Thread.interrupted());
LockSupport.park();
System.out.println("本打印出現在第二個park()之後");
}
}
}
結果:
本打印出現在第一個park()之後
true
false
3.1 分析
Thread中斷方法分析(包括HotSpot源碼)中Thread.interrupted()源碼:
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
bool os::is_interrupted(Thread* thread, bool clear_interrupted) {
assert(Thread::current() == thread || Threads_lock->owned_by_self(),
"possibility of dangling Thread pointer");
OSThread* osthread = thread->osthread();
bool interrupted = osthread->interrupted();
if (interrupted && clear_interrupted) {
osthread->set_interrupted(false);
// consider thread->_SleepEvent->reset() ... optional optimization
}
return interrupted;
}
可以看到如果中斷了並且clear_interrupted爲true,則會清除中斷標誌位。
3.2 總結
Thread.interrupted()清除中斷標誌位後,第二個LockSupport.park()就不會直接返回,而是正常地阻塞。
4.sleep清除中斷標誌位但不會更改_counter
public class MultInterruptParkDemo4 {
public static volatile boolean flag = true;
public static void main(String[] args) {
ThreadDemo04 t4 = new ThreadDemo04();
t4.start();
t4.interrupt();
flag = false;
}
public static class ThreadDemo04 extends Thread {
@Override
public void run() {
while (flag) {
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("本打印出現在第一個sleep()之後");
System.out.println(Thread.interrupted());
System.out.println(Thread.interrupted());
LockSupport.park();
System.out.println("本打印出現在第二個park()之後");
}
}
}
結果
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at wz.interrupt.MultInterruptParkDemo4$ThreadDemo04.run(MultInterruptParkDemo4.java:19)
本打印出現在第一個sleep()之後
false
false
本打印出現在第二個park()之後
4.1 分析
Thread中斷方法分析(包括HotSpot源碼)中interrupt源碼:
void os::interrupt(Thread* thread) {
assert(Thread::current() == thread || Threads_lock->owned_by_self(),
"possibility of dangling Thread pointer");
OSThread* osthread = thread->osthread();
if (!osthread->interrupted()) {
osthread->set_interrupted(true);
// More than one thread can get here with the same value of osthread,
// resulting in multiple notifications. We do, however, want the store
// to interrupted() to be visible to other threads before we execute unpark().
OrderAccess::fence();
ParkEvent * const slp = thread->_SleepEvent ;
if (slp != NULL) slp->unpark() ;
}
// For JSR166. Unpark even if interrupt status already was set
if (thread->is_Java_thread())
((JavaThread*)thread)->parker()->unpark();
ParkEvent * ev = thread->_ParkEvent ;
if (ev != NULL) ev->unpark() ;
}
會調用Parker::unpark(),那麼_counter會設置爲1.
參考阻塞線程的相關方法分析(包括HotSpot層源碼)中關於sleep源碼:
JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis))
JVMWrapper("JVM_Sleep");
if (millis < 0) {
THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
}
if (Thread::is_interrupted (THREAD, true) && !HAS_PENDING_EXCEPTION) {
THROW_MSG(vmSymbols::java_lang_InterruptedException(), "sleep interrupted");
}
如果線程已中斷,則清除中斷標記並拋出中斷異常,直接返回,但是並沒有更改_counter。
4.2 總結
- interrupt會調用((JavaThread*)thread)->parker()->unpark(),將_counter設置爲1,也即後面調用park不會阻塞
- sleep如果檢測到中斷會直接清除中斷標誌,並拋出異常。
- 因此兩個Thread.interrupted()都返回false,且LockSupport.park()不會阻塞。
5.對於LockSupport.park()阻塞總結
如下兩個條件任何一個成立,park()都不會阻塞:
- 中斷標誌位存在(wait、join、sleep或Thread.interrupted()都會清除中斷標誌位)
- _counter爲1(之前調用了unpark或者interrupt)