1.Thread.interrupt
1.1 官方文档
public void interrupt() {
if (this != Thread.currentThread())
checkAccess();
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // Just to set the interrupt flag
b.interrupt(this);
return;
}
}
interrupt0();
}
Interrupts this thread.
Unless the current thread is interrupting itself, which is always
permitted, the checkAccess method of this thread is invoked, which
may cause a SecurityException to be thrown.
If this thread is blocked in an invocation of the wait(), wait(long), or
wait(long, int) methods of the Object class, or of the join(), join(long),
join(long, int), sleep(long), or sleep(long, int), methods of this class,
then its interrupt status will be cleared and it will receive an
InterruptedException.
If this thread is blocked in an I/O operation upon an
InterruptibleChannel then the channel will be closed, the thread's
interrupt status will be set, and the thread will receive a
ClosedByInterruptException.
If this thread is blocked in a Selector then the thread's interrupt status
will be set and it will return immediately from the selection operation,
possibly with a non-zero value, just as if the selector's wakeup
method were invoked.
If none of the previous conditions hold then this thread's interrupt
status will be set.
Interrupting a thread that is not alive need not have any effect.
中断此线程。
除非当前线程正在中断自身(始终允许),否则将调用此线程的checkAccess方法,这可能导致抛出SecurityException。
如果该线程阻塞在如下方法的调用:Object类的wait()、wait(long)或wait(long,int)方法,或者Thread类的join()、join(long)、join(long, int)方法、sleep(long)或sleep(long, int)方法,则它的中断状态将被清除,并将收到InterruptedException。
如果线程阻塞在InterruptibleChannel上的I/O操作,则该通道将被关闭,线程的中断状态将被置位,并且线程将收到ClosedByInterruptException。
如果此线程阻塞在Selector中,则线程的中断状态将被置位,并且它将立即从选择操作返回,可能具有非零值,就像调用选择器的唤醒方法一样。
如果上面的条件都不成立,则将置位该线程的中断状态。
中断不活动的线程不会产生任何影响。
1.2 interrupt0本地方法
wz@wz-All-Series:~/openjdk/jdk8u-src/jdk/src/share/native$ grep -rn "interrupt0"
java/lang/Thread.c:54: {"interrupt0", "()V", (void *)&JVM_Interrupt},
位置openjdk/jdk8u-src/jdk/src/share/native/java/lang/Thread.c
static JNINativeMethod methods[] = {
{"start0", "()V", (void *)&JVM_StartThread},
{"stop0", "(" OBJ ")V", (void *)&JVM_StopThread},
{"isAlive", "()Z", (void *)&JVM_IsThreadAlive},
{"suspend0", "()V", (void *)&JVM_SuspendThread},
{"resume0", "()V", (void *)&JVM_ResumeThread},
{"setPriority0", "(I)V", (void *)&JVM_SetThreadPriority},
{"yield", "()V", (void *)&JVM_Yield},
{"sleep", "(J)V", (void *)&JVM_Sleep},
{"currentThread", "()" THD, (void *)&JVM_CurrentThread},
{"countStackFrames", "()I", (void *)&JVM_CountStackFrames},
{"interrupt0", "()V", (void *)&JVM_Interrupt},
{"isInterrupted", "(Z)Z", (void *)&JVM_IsInterrupted},
{"holdsLock", "(" OBJ ")Z", (void *)&JVM_HoldsLock},
{"getThreads", "()[" THD, (void *)&JVM_GetAllThreads},
{"dumpThreads", "([" THD ")[[" STE, (void *)&JVM_DumpThreads},
{"setNativeName", "(" STR ")V", (void *)&JVM_SetNativeThreadName},
};
JNIEXPORT void JNICALL
Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)
{
(*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}
wz@wz-All-Series:~/openjdk/jdk8u-src$ grep -rn "JVM_Interrupt" .
./hotspot/src/share/vm/prims/jvm.cpp:3347:JVM_ENTRY(void, JVM_Interrupt(JNIEnv* env, jobject jthread))
// Consider: A better way to implement JVM_Interrupt() is to acquire
// Threads_lock to resolve the jthread into a Thread pointer, fetch
// Thread->platformevent, Thread->native_thr, Thread->parker, etc.,
// drop Threads_lock, and the perform the unpark() and thr_kill() operations
// outside the critical section. Threads_lock is hot so we want to minimize
// the hold-time. A cleaner interface would be to decompose interrupt into
// two steps. The 1st phase, performed under Threads_lock, would return
// a closure that'd be invoked after Threads_lock was dropped.
// This tactic is safe as PlatformEvent and Parkers are type-stable (TSM) and
// admit spurious wakeups.
JVM_ENTRY(void, JVM_Interrupt(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_Interrupt");
// Ensure that the C++ Thread and OSThread structures aren't freed before we operate
oop java_thread = JNIHandles::resolve_non_null(jthread);
MutexLockerEx ml(thread->threadObj() == java_thread ? NULL : Threads_lock);
// We need to re-resolve the java_thread, since a GC might have happened during the
// acquire of the lock
JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread));
if (thr != NULL) {
Thread::interrupt(thr);
}
JVM_END
定位Thread::interrupt
wz@wz-All-Series:~/openjdk/jdk8u-src/hotspot/src$ grep -rn "Thread::interrupt" .
./share/vm/runtime/thread.cpp:804:void Thread::interrupt(Thread* thread) {
/share/vm/runtime/thread.cpp:
void Thread::interrupt(Thread* thread) {
trace("interrupt", thread);
debug_only(check_for_dangling_thread_pointer(thread);)
os::interrupt(thread);
}
到linux目录下查找:
wz@wz-All-Series:~/openjdk/jdk8u-src/hotspot/src/os/linux/vm$ grep -rn "os::interrupt" .
./os_linux.cpp:4462:void os::interrupt(Thread* thread) {
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() ;
}
- step1.通过thread->osthread()来获取系统线程
- step2.如果未设置中断标志,则通过osthread->set_interrupted(true)来设置;调用 OrderAccess::fence()屏障指令,保证在调用unpark之前对其他线程可见。
调用slp->unpark()中断Thread.sleep()。 - step3.((JavaThread*)thread)->parker()->unpark()中断LockSupport.park()。
- step4.ev->unpark()中断Object.wait()的方法。
class OSThread: public CHeapObj {
private:
volatile jint _interrupted; // Thread.isInterrupted state
public:
volatile bool interrupted() const { return _interrupted != 0; }
void set_interrupted(bool z) { _interrupted = z ? 1 : 0; }
1.2.1 ParkEvent
wz@wz-All-Series:~/openjdk/jdk8u-src$ grep -rn "class ParkEvent" .
./hotspot/src/share/vm/runtime/park.hpp:118:class ParkEvent : public os::PlatformEvent {
class ParkEvent : public os::PlatformEvent {
private:
ParkEvent * FreeNext ;
// Current association
Thread * AssociatedWith ;
intptr_t RawThreadIdentity ; // LWPID etc
volatile int Incarnation ;
// diagnostic : keep track of last thread to wake this thread.
// this is useful for construction of dependency graphs.
void * LastWaker ;
public:
// MCS-CLH list linkage and Native Mutex/Monitor
ParkEvent * volatile ListNext ;
ParkEvent * volatile ListPrev ;
volatile intptr_t OnList ;
volatile int TState ;
volatile int Notified ; // for native monitor construct
volatile int IsWaiting ; // Enqueued on WaitSet
private:
static ParkEvent * volatile FreeList ;
static volatile int ListLock ;
// It's prudent to mark the dtor as "private"
// ensuring that it's not visible outside the package.
// Unfortunately gcc warns about such usage, so
// we revert to the less desirable "protected" visibility.
// The other compilers accept private dtors.
protected: // Ensure dtor is never invoked
~ParkEvent() { guarantee (0, "invariant") ; }
ParkEvent() : PlatformEvent() {
AssociatedWith = NULL ;
FreeNext = NULL ;
ListNext = NULL ;
ListPrev = NULL ;
OnList = 0 ;
TState = 0 ;
Notified = 0 ;
IsWaiting = 0 ;
}
// We use placement-new to force ParkEvent instances to be
// aligned on 256-byte address boundaries. This ensures that the least
// significant byte of a ParkEvent address is always 0.
void * operator new (size_t sz) throw();
void operator delete (void * a) ;
public:
static ParkEvent * Allocate (Thread * t) ;
static void Release (ParkEvent * e) ;
} ;
wz@wz-All-Series:~/openjdk/jdk8u-src$ grep -rn "class PlatformEvent" .
./hotspot/src/os/linux/vm/os_linux.hpp:355:class PlatformEvent : public CHeapObj {
class PlatformEvent : public CHeapObj {
private:
double CachePad [4] ; // increase odds that _mutex is sole occupant of cache line
volatile int _Event ;
volatile int _nParked ;
pthread_mutex_t _mutex [1] ;
pthread_cond_t _cond [1] ;
double PostPad [2] ;
Thread * _Assoc ;
public: // TODO-FIXME: make dtor private
~PlatformEvent() { guarantee (0, "invariant") ; }
public:
PlatformEvent() {
int status;
status = pthread_cond_init (_cond, os::Linux::condAttr());
assert_status(status == 0, status, "cond_init");
status = pthread_mutex_init (_mutex, NULL);
assert_status(status == 0, status, "mutex_init");
_Event = 0 ;
_nParked = 0 ;
_Assoc = NULL ;
}
// Use caution with reset() and fired() -- they may require MEMBARs
void reset() { _Event = 0 ; }
int fired() { return _Event; }
void park () ;
void unpark () ;
int TryPark () ;
int park (jlong millis) ; // relative timed-wait only
void SetAssociation (Thread * a) { _Assoc = a ; }
} ;
void os::PlatformEvent::unpark() {
// Transitions for _Event:
// 0 :=> 1
// 1 :=> 1
// -1 :=> either 0 or 1; must signal target thread
// That is, we can safely transition _Event from -1 to either
// 0 or 1. Forcing 1 is slightly more efficient for back-to-back
// unpark() calls.
// See also: "Semaphores in Plan 9" by Mullender & Cox
//
// Note: Forcing a transition from "-1" to "1" on an unpark() means
// that it will take two back-to-back park() calls for the owning
// thread to block. This has the benefit of forcing a spurious return
// from the first park() call after an unpark() call which will help
// shake out uses of park() and unpark() without condition variables.
if (Atomic::xchg(1, &_Event) >= 0) return;
// Wait for the thread associated with the event to vacate
int status = pthread_mutex_lock(_mutex);
assert_status(status == 0, status, "mutex_lock");
int AnyWaiters = _nParked;
assert(AnyWaiters == 0 || AnyWaiters == 1, "invariant");
if (AnyWaiters != 0 && WorkAroundNPTLTimedWaitHang) {
AnyWaiters = 0;
pthread_cond_signal(_cond);
}
status = pthread_mutex_unlock(_mutex);
assert_status(status == 0, status, "mutex_unlock");
if (AnyWaiters != 0) {
status = pthread_cond_signal(_cond);
assert_status(status == 0, status, "cond_signal");
}
// Note that we signal() _after dropping the lock for "immortal" Events.
// This is safe and avoids a common class of futile wakeups. In rare
// circumstances this can cause a thread to return prematurely from
// cond_{timed}wait() but the spurious wakeup is benign and the victim will
// simply re-test the condition and re-park itself.
}
- step1.这里将_Event设置为1
- step2.后面加锁_mutex来操作_nParked,如果不为0,则将其设置为0,并pthread_cond_signal(_cond)唤醒阻塞的线程
2.Thread.interrupted及Thread.isInterrupted
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
Tests whether the current thread has been interrupted. The
interrupted status of the thread is cleared by this method. In other
words, if this method were to be called twice in succession, the
second call would return false (unless the current thread were
interrupted again, after the first call had cleared its interrupted status
and before the second call had examined it).
A thread interruption ignored because a thread was not alive at the
time of the interrupt will be reflected by this method returning false.
测试当前线程是否已被中断。 此方法清除线程的中断状态。 换句话说,如果要连续两次调用此方法,则第二次调用将返回false(除非当前线程在第一次调用清除中断状态后且在第二次调用检查之前再次中断)。
中断不活动线程将被忽略,并且此方法返回false。
public boolean isInterrupted() {
return isInterrupted(false);
}
测试此线程是否已被中断。 线程的中断状态不受此方法的影响。
中断不活动线程将被忽略,并且此方法返回false。
private native boolean isInterrupted(boolean ClearInterrupted);
./hotspot/src/share/vm/prims/jvm.cpp:
JVM_QUICK_ENTRY(jboolean, JVM_IsInterrupted(JNIEnv* env, jobject jthread, jboolean clear_interrupted))
JVMWrapper("JVM_IsInterrupted");
// Ensure that the C++ Thread and OSThread structures aren't freed before we operate
oop java_thread = JNIHandles::resolve_non_null(jthread);
MutexLockerEx ml(thread->threadObj() == java_thread ? NULL : Threads_lock);
// We need to re-resolve the java_thread, since a GC might have happened during the
// acquire of the lock
JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread));
if (thr == NULL) {
return JNI_FALSE;
} else {
return (jboolean) Thread::is_interrupted(thr, clear_interrupted != 0);
}
JVM_END
bool Thread::is_interrupted(Thread* thread, bool clear_interrupted) {
trace("is_interrupted", thread);
debug_only(check_for_dangling_thread_pointer(thread);)
// Note: If clear_interrupted==false, this simply fetches and
// returns the value of the field osthread()->interrupted().
return os::is_interrupted(thread, clear_interrupted);
}
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;
}
- step1.通过thread->osthread()来获取系统线程,并获取中断标识interrupted = osthread->interrupted()
- step2.如果线程已经被中断并且clear_interrupted为true,则清除中断标志位。其他情况只是返回中断标识位。
这也说明了为什么interrupted()为什么会清除中断标志位,因为其调用的是currentThread().isInterrupted(true),true表示要清除。
wz@wz-All-Series:~/openjdk/jdk8u-src/hotspot/src$ grep -rn "class OSThread" .
./share/vm/runtime/osThread.hpp:61:class OSThread: public CHeapObj {
class OSThread: public CHeapObj {
private:
volatile jint _interrupted; // Thread.isInterrupted state
public:
volatile bool interrupted() const { return _interrupted != 0; }
void set_interrupted(bool z) { _interrupted = z ? 1 : 0; }