java线程原理
Java线程原理#
单词 thread
原意是 线
, 在计算机科学引申为 一串执行过程组成的线, 线程是可以由调度程序独立管理的最小编程指令序列. 在一般情况下, 线程属于进程, 同一进程的线程可以同时执行, 共享内存等资源.
具体到 Java 的线程, 即 java.lang.Thread
类, 并不能与操作系统的线程等价. Java 线程由 JVM 实现并调度, 进程切换由 JVM 在用户空间完成, 相比之下开销更小, 同步机制也由 JVM 实现.
为什么需要线程#
线程的需求源于系统对同时执行多个任务的需求. 同时, 多线程也能利用上多核CPU进行并行计算.
在操作系统中, 多进程也能完成同时执行多个任务, 但相比进程, 线程更轻量级, 线程之间可以共享内存, 线程的切换比进程切换的开销更小.
使用#
线程在调用 start
之后, 该线程进入 RUNNABLE
状态, 在获得的CPU资源后, run
方法将会执行.
线程如何调度本身并不由开发者决定, 对于开发者来说, 线程的使用重点是关注线程需要执行的任务, 即 run
方法中所执行的内容. 在默认情况下, run
方法会构造方法执行传入的 Runnable
类型的任务对象.
下面是一个线程最为基本的使用例子:
public class TestThread {
public static void main(String[] args) throws InterruptedException {
var thread =
new Thread(
() -> {
System.out.println("Hello World!");
});
thread.start();
System.out.println("thread started");
thread.join();
System.out.println("thread finished");
}
}
thread started
Hello World!
thread finished
然而实际大多数情况不会这么简单(虽然也不是不能用), 不过本文的主题是线程的实现, 如何使用不是主题, 以后有机会再写写看.
Java源码概要#
下文中涉及的源码基于 JDK17.
对于开发者比较重要的一些字段和方法
public class Thread implements Runnable {
// 线程名称
private volatile String name;
// 优先级
private int priority;
// 是否守护线程
private boolean daemon = false;
// 中断状态
private volatile boolean interrupted;
// 运行的任务
private Runnable target;
// 线程的所属组
private ThreadGroup group;
// 该线程的上下文类加载器, 用于SPI
private ClassLoader contextClassLoader;
// 当前线程的 ThreadLocal 变量集合, 该字段由 ThreadLocal 类维护
ThreadLocal.ThreadLocalMap threadLocals = null;
// 当前线程的 InheritableThreadLocal 变量集合, 该字段由 InheritableThreadLocal 类维护
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
// 线程ID
private final long tid;
// 线程状态, 可以通过jdk.internal.misc.VM.toThreadState(threadStatus)获取Thread.State类表示的状态
private volatile int threadStatus;
// 启动线程
public synchronized void start();
// 线程运行执行的代码
public void run();
// 发出中断信号
public void interrupt();
// 线程是否存活, 线程 start 以后, 还未终止就是存活
public final native boolean isAlive();
// 另当前线程等待目标线程运行结束
public final void join();
// 获取当前线程
public static native Thread currentThread();
// 获取当前线程的中断状态并重设
public static boolean interrupted();
}
Hotspot中的线程#
Java的线程需要JVM的线程实现, hotspot中定义了一些线程类型, 其类型关系如下
- Thread
- JavaThread
- various subclasses eg CompilerThread, ServiceThread
- NonJavaThread
- NamedThread
- VMThread
- ConcurrentGCThread
- WorkerThread
- GangWorker
- WatcherThread
- JfrThreadSampler
- LogAsyncWriter
一个JVM线程的实例必定为 JavaThread
或 NonJavaThread
.
而在Java中, 通过直接 new Thread
或其自定义子类所创建的Java线程对象, 在调用 start
之后创建是 JavaThread
类型的 JVM 线程.
线程创建和执行及结束的全流程#
创建#
Java线程对象的创建只需调用 Thread
类或子类的构造方法.
var thread = new Thread(runnable);
启动#
线程在调用 start
方法后启动, 源码如下:
public synchronized void start() {
// threadStatus 为0时, 对应 Thread.State 为 NEW 的状态
// 一个线程不能多次 start, 否则抛出异常
if (threadStatus != 0)
throw new IllegalThreadStateException();
// 将线程加入线程组
group.add(this);
boolean started = false;
try {
// JNI调用, 由JVM启动线程
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
JVM 创建的调用 main
方法的线程以及属于 system
线程组的线程不会调用 start
方法.
线程组是一个线程集合, 用于批量管理线程. 线程组有继承关系, 线程组的最终父级是
system
线程组.以下代码可以打印
system
线程组和子线程组的线程信息:Thread.currentThread().getThreadGroup().getParent().list();
java.lang.ThreadGroup[name=system,maxpri=10] Thread[Reference Handler,10,system] Thread[Finalizer,8,system] Thread[Signal Dispatcher,9,system] Thread[Attach Listener,5,system] Thread[Notification Thread,9,system] java.lang.ThreadGroup[name=main,maxpri=10] Thread[main,5,main] Thread[Monitor Ctrl-Break,5,main] java.lang.ThreadGroup[name=InnocuousThreadGroup,maxpri=10] Thread[Common-Cleaner,8,InnocuousThreadGroup]
可以看到, 主线程属于
main
线程组, 而由主线程所创建的线程在默认情况下和主线程属于同一个线程组.
在 start
方法中, 真正的启动线程的在 start0
中.
本地方法 start0
以及 Thread
类中的其他本地方法是由静态方法 Thread.registerNatives()
在类加载的时候注册到JNI环境中的, 在JDK中源码(Thread.c文件)如下:
static JNINativeMethod methods[] = {
{"start0", "()V", (void *)&JVM_StartThread},
{"setPriority0", "(I)V", (void *)&JVM_SetThreadPriority},
{"yield0", "()V", (void *)&JVM_Yield},
{"sleep0", "(J)V", (void *)&JVM_Sleep},
{"currentCarrierThread", "()" THD, (void *)&JVM_CurrentCarrierThread},
{"currentThread", "()" THD, (void *)&JVM_CurrentThread},
{"setCurrentThread", "(" THD ")V", (void *)&JVM_SetCurrentThread},
{"interrupt0", "()V", (void *)&JVM_Interrupt},
{"holdsLock", "(" OBJ ")Z", (void *)&JVM_HoldsLock},
{"getThreads", "()[" THD, (void *)&JVM_GetAllThreads},
{"dumpThreads", "([" THD ")[[" STE, (void *)&JVM_DumpThreads},
{"getStackTrace0", "()" OBJ, (void *)&JVM_GetStackTrace},
{"setNativeName", "(" STR ")V", (void *)&JVM_SetNativeThreadName},
{"scopedValueCache", "()[" OBJ, (void *)&JVM_ScopedValueCache},
{"setScopedValueCache", "([" OBJ ")V",(void *)&JVM_SetScopedValueCache},
{"getNextThreadIdOffset", "()J", (void *)&JVM_GetNextThreadIdOffset},
{"findScopedValueBindings", "()" OBJ, (void *)&JVM_FindScopedValueBindings},
{"ensureMaterializedForStackWalk",
"(" OBJ ")V", (void*)&JVM_EnsureMaterializedForStackWalk_func},
};
JNIEXPORT void JNICALL
Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls)
{
(*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));
}
start0
对应的就是 JVM_StartThread
的函数指针, 函数源码在 jvm.cpp 文件中.
JVM_StartThread
函数所做的事情主要是:
-
创建
JavaThread
并初始化JVM_StartThread
所创建的C++线程是JavaThread
(C++) 类的实例, 会持有一个函数指针entry_point
, 指向 jvm.cpp 文件中的thread_entry
函数.native_thread = new JavaThread(&thread_entry, sz);
static void thread_entry(JavaThread* thread, TRAPS) { HandleMark hm(THREAD); Handle obj(THREAD, thread->threadObj()); JavaValue result(T_VOID); // invoke_virtual 调用Java平台线程的run方法 JavaCalls::call_virtual(&result, obj, vmClasses::Thread_klass(), vmSymbols::run_method_name(), vmSymbols::void_method_signature(), THREAD); }
JavaThread
的实例化函数会调用os::create_thread
创建操作系统线程, 其在 windows 平台的实现在os_windows.cpp
文件中.JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) : JavaThread() { _jni_attach_state = _not_attaching_via_jni; // 设置线程要执行的函数 set_entry_point(entry_point); os::ThreadType thr_type = os::java_thread; // CompilerThread 是JIT编译器的线程 thr_type = entry_point == &CompilerThread::thread_entry ? os::compiler_thread : os::java_thread; // 创建os线程 os::create_thread(this, thr_type, stack_sz); }
// os_windows.cpp bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) { unsigned thread_id; // 创建和初始化OS线程 OSThread* osthread = new (std::nothrow) OSThread(); if (osthread == nullptr) { return false; } osthread->set_state(ALLOCATED); // jdk 中断事件支持(windows平台) HANDLE interrupt_event = CreateEvent(nullptr, true, false, nullptr); if (interrupt_event == nullptr) { delete osthread; return false; } osthread->set_interrupt_event(interrupt_event); // Java 平台Thread -> JVM JavaThread -> OS Thread thread->set_osthread(osthread); if (stack_size == 0) { switch (thr_type) { case os::java_thread: // -Xss可以设置最小栈大小 if (JavaThread::stack_size_at_create() > 0) { stack_size = JavaThread::stack_size_at_create(); } break; case os::compiler_thread: if (CompilerThreadStackSize > 0) { stack_size = (size_t)(CompilerThreadStackSize * K); break; } case os::vm_thread: case os::gc_thread: case os::asynclog_thread: case os::watcher_thread: if (VMThreadStackSize > 0) stack_size = (size_t)(VMThreadStackSize * K); break; } } // windows的线程flag const unsigned initflag = CREATE_SUSPENDED | STACK_SIZE_PARAM_IS_A_RESERVATION; // 线程句柄 HANDLE thread_handle; int limit = 3; do { thread_handle = // windows创建线程的系统调用 (HANDLE)_beginthreadex(nullptr, (unsigned)stack_size, // 操作系统线程将执行 thread_native_entry &os::win32::thread_native_entry, // thread_native_entry 的参数 thread, // 创建线程的flag, 此处是挂起并预留栈空间 initflag, &thread_id); } while (thread_handle == nullptr && errno == EAGAIN && limit-- > 0); ResourceMark rm; char buf[64]; if (thread_handle != nullptr) { log_info(os, thread)("Thread \"%s\" started (tid: %u, attributes: %s)", thread->name(), thread_id, describe_beginthreadex_attributes(buf, sizeof(buf), stack_size, initflag)); } else { log_warning(os, thread)("Failed to start thread \"%s\" - _beginthreadex failed (%s) for attributes: %s.", thread->name(), os::errno_name(errno), describe_beginthreadex_attributes(buf, sizeof(buf), stack_size, initflag)); log_info(os, thread)("Number of threads approx. running in the VM: %d", Threads::number_of_threads()); LogStream st(Log(os, thread)::info()); os::print_memory_info(&st); } // 创建失败处理 if (thread_handle == nullptr) { thread->set_osthread(nullptr); delete osthread; return false; } // 创建成功设置OS Thread字段 osthread->set_thread_handle(thread_handle); osthread->set_thread_id(thread_id); osthread->set_state(INITIALIZED); return true; }
-
调用
Thread::start
启动线程Thread::start
设置 Java 的 Thread 类的threadState
字段的值为RUNNABLE
对应的值, 并通过os::start_thread
运行操作系统线程.void Thread::start(Thread* thread) { if (thread->is_Java_thread()) { // 设置Java平台线程对象的状态 java_lang_Thread::set_thread_status(JavaThread::cast(thread)->threadObj(), JavaThreadStatus::RUNNABLE); } // 启动OS线程 os::start_thread(thread); }
void os::start_thread(Thread* thread) { OSThread* osthread = thread->osthread(); osthread->set_state(RUNNABLE); // 启动线程, 不同平台实现不同 pd_start_thread(thread); }
运行和退出#
操作系统线程运行的是 thread_native_entry
函数, 该函数的参数是前一步所创建的 C++ 线程对象.
thread_native_entry
是JVM线程执行的函数入口, 该函数内部调用了线程类的 call_run
函数, 该函数会按顺序调用线程的 pre_run
, run
, post_run
函数.
对于 JavaThread
的实现来说:
-
pre_run
函数为空什么也不做 -
run
函数按顺序执行一下过程:- 初始化
TLAB
- 调用
thread_main_inner
方法,thread_main_inner
方法中将会调用JavaThread
所持有的entry_point
函数指针.entry_point
是thread_entry
的函数指针, 该函数将会通过invoke_virtual
指令调用Java平台的线程类对象的run
方法.
void JavaThread::run() { // 初始化TLAB initialize_tlab(); // ... 其它还没搞懂的代码 // 执行构造 JavaThread 时的函数指针 entry_point thread_main_inner(); }
void JavaThread::thread_main_inner() { assert(JavaThread::current() == this, "sanity check"); assert(_threadObj.peek() != nullptr, "just checking"); if (!this->has_pending_exception()) { { ResourceMark rm(this); this->set_native_thread_name(this->name()); } HandleMark hm(this); // 执行构造 JavaThread 时传入的函数指针 this->entry_point()(this, this); } DTRACE_THREAD_PROBE(stop, this); }
- 初始化
-
post_run
函数- 调用
JavaThread
的exit
函数, 如果有异常,exit
函数将会对退出的异常进行处理, 在正常退出的情况下, 将会通过invoke_virtual
指令调用Java平台的线程类对象的exit
方法 - 设置Java平台线程的状态为终止
- 通知所有
join
了当前线程的线程 - 其它清理工作
void JavaThread::post_run() { this->exit(false); this->unregister_thread_stack_with_NMT(); // jstack会显示线程SMR信息 this->smr_delete(); }
void JavaThread::exit(bool destroy_vm, ExitType exit_type) { /* 其它代码*/ if (!destroy_vm) { if (uncaught_exception.not_null()) { EXCEPTION_MARK; // 调用Java线程的dispatchUncaughtException方法处理未捕获的异常 Klass* thread_klass = vmClasses::Thread_klass(); JavaValue result(T_VOID); JavaCalls::call_virtual(&result, threadObj, thread_klass, vmSymbols::dispatchUncaughtException_name(), vmSymbols::throwable_void_signature(), uncaught_exception, THREAD); if (HAS_PENDING_EXCEPTION) { ResourceMark rm(this); jio_fprintf(defaultStream::error_stream(), "\nException: %s thrown from the UncaughtExceptionHandler" " in thread \"%s\"\n", pending_exception()->klass()->external_name(), name()); CLEAR_PENDING_EXCEPTION; } } if (!is_Compiler_thread()) { int count = 3; while (java_lang_Thread::threadGroup(threadObj()) != NULL && (count-- > 0)) { EXCEPTION_MARK; // 调用Java线程的exit方法 JavaValue result(T_VOID); Klass* thread_klass = vmClasses::Thread_klass(); JavaCalls::call_virtual(&result, threadObj, thread_klass, vmSymbols::exit_method_name(), vmSymbols::void_method_signature(), THREAD); CLEAR_PENDING_EXCEPTION; } } if (JvmtiExport::should_post_thread_life()) { JvmtiExport::post_thread_end(this); } } else { assert(!is_terminated() && !is_exiting(), "must not be exiting"); } /* 其它代码 */ // notify所有通过 join 阻塞的线程 ensure_join(this); /* 其它代码 */ }
private void exit() { if (threadLocals != null && TerminatingThreadLocal.REGISTRY.isPresent()) { TerminatingThreadLocal.threadTerminated(); } if (group != null) { group.threadTerminated(this); group = null; } /* Aggressively null out all reference fields: see bug 4006245 */ target = null; /* Speed the release of some of these resources */ threadLocals = null; inheritableThreadLocals = null; inheritedAccessControlContext = null; blocker = null; uncaughtExceptionHandler = null; }
- 调用
线程 join
的原理#
Thread
类的 join
可以让执行调用时的线程阻塞等待调用该方法的线程完成.
var thread = new Thread(() -> System.out.println("running"));
thread.start();
thread.join(); // 阻塞直到线程执行完成(前文提到的 `post_run` 函数的退出通知完成)
System.out.println("finished");
join
方法源码:
源码注释:
最多等待
millis
毫秒到线程终止, 如果时间为0则永远等待.通过以
this.isAlive
为条件循环调用this.wait
实现, 当线程终止时, 将调用this.notifyAll
方法.建议应用程序不要在
Thread
实例上使用wait
,notify
和notifyAll
.
public final synchronized void join(final long millis) throws InterruptedException {
if (millis > 0) {
if (isAlive()) {
final long startTime = System.nanoTime();
long delay = millis;
do {
wait(delay); // this.wait(millis) 锁住的就是当前 thread 对象的监视器
} while (isAlive() && (delay = millis -
TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)) > 0);
} // else 如果线程已经终止, 就直接返回
} else if (millis == 0) {
while (isAlive()) {
wait(0);
} // else 如果线程已经终止, 就直接返回
} else {
throw new IllegalArgumentException("timeout value is negative");
}
}
可以看到, join
本质上就是调用了当前线程从 Object
类继承的 wait
方法, 如果被唤醒, 如果线程还未结束(带超时时间的则会也判断是否超时), 将继续调用 wait
阻塞, 直至线程完成, 而线程完成的时候, 会由 JVM 对该线程的监视器发出通知唤醒.
那么在看完上面的源码后回答一个问题: 对当前线程 Thread.currentThread().join()
会发生什么? 答案是永远阻塞, 除非调用该线程的 interrupt
方法.
public class TestJoin {
public static void main(String[] args) throws InterruptedException {
var mainThread = Thread.currentThread();
var thread =
new Thread(
() -> {
sleep(1000);
System.out.println("before notify");
synchronized (mainThread) {
mainThread.notify();
}
sleep(1000);
System.out.println("notified");
System.out.println("before interrupt");
mainThread.interrupt();
});
thread.start();
try {
mainThread.join();
} catch (InterruptedException e) {
System.out.println("interrupt");
}
}
private static void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
如果你在 join
方法中的 wait(0)
和 那一行以及上面这段代码的 mainThread.notify()
打上断点, 并Debug 执行上面这段代码, 可以发现首次 wait(0)
之后, 再执行 notify
, mainThread
又会继续运行并再次调用 wait(0)
, 而在 interrupt
之后, wait(0)
就会抛出 InterruptedException
, 在被捕获之后执行异常处理.
线程 interrupt
的原理#
Java线程的 interrupt
方法可以中断线程, 这种中断并非中止, 中断只能中断线程的特点操作. 源码如下:
源码注释(机翻警告):
中断该线程。
除非当前线程正在中断自身(这始终是允许的),否则将调用该线程的checkAccess方法,这可能会导致抛出SecurityException 。
如果此线程在调用Object类的wait() 、 wait(long)或wait(long, int)方法或join() 、 join(long) 、 join(long, int)时被阻止该类的 、 sleep(long)或sleep(long, int)方法,则其中断状态将被清除,并且将收到InterruptedException 。
如果该线程在InterruptibleChannel上的 I/O 操作中被阻塞,则通道将被关闭,线程的中断状态将被设置,并且线程将收到java.nio.channels.ClosedByInterruptException 。
如果该线程在java.nio.channels.Selector中被阻塞,则该线程的中断状态将被设置,并且它将立即从选择操作返回,可能返回一个非零值,就像调用选择器的wakeup方法一样。
如果前面的条件都不成立,则该线程的中断状态将被设置。
中断不活动的线程不需要产生任何效果。
public void interrupt() {
if (this != Thread.currentThread()) {
checkAccess();
// 线程可能阻塞在调用 I/O
synchronized (blockerLock) {
// 不为空时, 代表线程阻塞在 I/O
Interruptible b = blocker;
if (b != null) {
interrupted = true;
interrupt0(); // inform VM of interrupt
b.interrupt(this);
return;
}
}
}
interrupted = true;
// inform VM of interrupt
interrupt0();
}
interrupt
可以中断的线程操作有:
-
会抛出
InterruptedException
的方法, 包括wait
,join
,sleep
等等.这些方法在被中断后, 将会抛出
InterruptedException
, 并且其中断标识即Thread
类的interrupted
字段将被设置为false
. -
会抛出
ClosedByInterruptException
的方法, 即InterruptibleChannel
的各种子类的 I/O 操作.这些方法在被中断后, 将会抛出
ClosedByInterruptException
, 并且其中断标识将设置为true
.private static void testChannelInterrupt() throws InterruptedException { var thread = new Thread( () -> { try { var serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress(8080)); serverSocketChannel.accept(); } catch (ClosedByInterruptException e) { // channel interrupt true System.out.println("channel interrupt " + Thread.currentThread().isInterrupted()); } catch (IOException e) { throw new RuntimeException(e); } }); thread.start(); Thread.sleep(1000); thread.interrupt(); Thread.sleep(1000); }
-
java.nio.channels.Selector
的阻塞操作. 在中断后, 中断标识将为true
, 但并不会抛出异常, 而是直接返回. 与Channel
的区别在于对Interruptible
实现的区别.// AbstractSelector 类阻塞I/O操作会调用该方法 protected final void begin() { if (interruptor == null) { // 线程中断时的操作 interruptor = new Interruptible() { public void interrupt(Thread ignore) { AbstractSelector.this.wakeup(); }}; } AbstractInterruptibleChannel.blockedOn(interruptor); Thread me = Thread.currentThread(); if (me.isInterrupted()) interruptor.interrupt(me); }
关于 Interruptible
下文会说明.
java.io
的 I/O 操作并不会会被 interrupt
中断.
如果在目标线程调用这些方法之前调用
interrupt
线程, 那么这些操作同样会被中断并产生对应的结果.
private static void testInterruptBeforeSleep() { // false System.out.println(Thread.currentThread().isInterrupted()); // true Thread.currentThread().interrupt(); System.out.println(Thread.currentThread().isInterrupted()); try { Thread.sleep(1000); } catch (InterruptedException e) { // interrupted false System.out.println("interrupted " + Thread.currentThread().isInterrupted()); } }
可中断IO的实现#
前文提到的第 2 和 第 3 种可中断的操作, 本质都是可中断I/O, 其实现借助于 Interruptible
和 Thread
类的 blocker
字段.
/* The object in which this thread is blocked in an interruptible I/O
* operation, if any. The blocker's interrupt method should be invoked
* after setting this thread's interrupt status.
*/
private volatile Interruptible blocker;
private final Object blockerLock = new Object();
/* Set the blocker field; invoked via jdk.internal.access.SharedSecrets
* from java.nio code
*/
static void blockedOn(Interruptible b) {
Thread me = Thread.currentThread();
synchronized (me.blockerLock) {
me.blocker = b;
}
}
在调用可中断的阻塞I/O操作前, 调用方将通过 blockedOn
方法设置线程的 blocker
字段.
Interruptible
只有一个 interrupt
方法, 本质是一个回调, 通过设置中断时的回调, 在调用线程的 interrupt
方法的时候就可以调用该回调中断 I/O 操作.
在调用 blockedOn
前, 可能线程就已经被中断了, 因此, 在 blockedOn
完成后都需要检查线程中断状态, 如果已经中断就直接调用 Interruptible
的 interrupt
方法.
例如 AbstractInterruptibleChannel
的 begin
方法源码:
protected final void begin() {
if (interruptor == null) {
interruptor = new Interruptible() {
public void interrupt(Thread target) {
synchronized (closeLock) {
if (closed)
return;
closed = true;
interrupted = target;
try {
// 线程 interrupt 的时候关闭 Channel
AbstractInterruptibleChannel.this.implCloseChannel();
} catch (IOException x) { }
}
}};
}
blockedOn(interruptor); // 设置线程的 blocker
Thread me = Thread.currentThread();
if (me.isInterrupted()) // 检查中断状态
interruptor.interrupt(me); // 调用 Interruptible 的 interrupt 方法
}
因此, 也可以自己来实现一个 Interruptible
:
public class TestInterrupt {
public static void main(String[] args) throws InterruptedException, IOException {
SharedSecrets.getJavaLangAccess()
.blockedOn(t -> System.out.println("Interruptible: " + t.isInterrupted()));
var mainThread = Thread.currentThread();
new Thread(mainThread::interrupt).start();
while (!Thread.interrupted()) {
System.out.println("main thread is not interrupted");
}
}
}
这段代码需要增加 --add-exports=java.base/jdk.internal.access=ALL-UNNAMED --add-exports=java.base/sun.nio.ch=ALL-UNNAMED
才可以编译运行.
在输出若干次 main thread is not interrupted
后将输出 Interruptible: true
.
interrupt0
的实现#
本地方法 interrupt0
对应的 CPP 函数指针将调用 JavaThread
的 interrupt
函数.
void JavaThread::interrupt() {
// All callers should have 'this' thread protected by a
// ThreadsListHandle so that it cannot terminate and deallocate
// itself.
debug_only(check_for_dangling_thread_pointer(this);)
// For Windows _interrupt_event
WINDOWS_ONLY(osthread()->set_interrupted(true);)
// For Thread.sleep
_SleepEvent->unpark();
// For JSR166 LockSupport.park
parker()->unpark();
// For ObjectMonitor and JvmtiRawMonitor
_ParkEvent->unpark();
}
_SleepEvent
和 _ParkEvent
都是 ParkEvent
类型, parker
函数则会返回一个 Parker
类的对象, 他们源码都有比较多的说明.
ParkEvent
和 Packer
都设计了一对 park
- unpark
操作, park
可以让线程挂起直到 unpark
.
以此实现了 Java 平台的 sleep
, wait
, join
以及 juc
中的一些可中断方法.
private static void testJucInterrupt() throws InterruptedException {
var reentrantLock = new ReentrantLock();
reentrantLock.lock();
var thread =
new Thread(
() -> {
try {
System.out.println("thread interrupt " + Thread.currentThread().isInterrupted());
reentrantLock.lockInterruptibly();
} catch (Exception e) {
System.out.println("lock interrupt");
}
});
thread.start();
thread.interrupt();
Thread.sleep(1000);
}
另外, Thread
类的 interrupted
会静态方法读取线程的中断状态并重设中断状态.
public static boolean interrupted() {
Thread t = currentThread();
boolean interrupted = t.interrupted;
// We may have been interrupted the moment after we read the field,
// so only clear the field if we saw that it was set and will return
// true; otherwise we could lose an interrupt.
if (interrupted) {
t.interrupted = false;
clearInterruptEvent();
}
return interrupted;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用