当我们想要线程按照一定的执行顺序执行,通常最简单直接的方式就是使用到join方法。
join属于对象方法,通过线程实例可以调用。他的作用是阻塞等待线程执行结束,再执行后续的代码。接下来展开说明join的实现原理。
线程的waiting状态
JAVA中能时线程进入waiting的方法有obj.wait(),thread.join(),LockSupport.park()。waiting状态的线程需要被唤醒转化为runnable状态,等待CPU调度。而唤醒线程的方法通过执行notify/notifyAll方法,JVM调用操作系统命令实现。
我们来看下join方法
public final void join() throws InterruptedException {
join(0);
}
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
// 参数为0 调用
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
// 实际上调用的是Object的wait()方法
public final native void wait(long timeout) throws InterruptedException;
可以知道,join方法是通过不断的循环调用wait()方法是实现等待,那么什么时候会唤醒线程继续执行?join的作用是等待线程执行完成,那么可以猜想,再线程结束执行时是否会唤醒线程?
我们看下Thread的start方法,实际上调用的是native方法start0(),这时需要看下JDK源码下Thread.c找到start0方法定义
{"start0", "()V", (void *)&JVM_StartThread},
再查看hotspot源码,跟踪到jvm.cpp
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_StartThread");
JavaThread *native_thread = NULL;
// We cannot hold the Threads_lock when we throw an exception,
// due to rank ordering issues. Example: we might need to grab the
// Heap_lock while we construct the exception.
bool throw_illegal_thread_state = false;
// We must release the Threads_lock before we can post a jvmti event
// in Thread::start.
{
// Ensure that the C++ Thread and OSThread structures aren't freed before
// we operate.
MutexLocker mu(Threads_lock);
// Since JDK 5 the java.lang.Thread threadStatus is used to prevent
// re-starting an already started thread, so we should usually find
// that the JavaThread is null. However for a JNI attached thread
// there is a small window between the Thread object being created
// (with its JavaThread set) and the update to its threadStatus, so we
// have to check for this
if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
throw_illegal_thread_state = true;
} else {
// We could also check the stillborn flag to see if this thread was already stopped, but
// for historical reasons we let the thread detect that itself when it starts running
jlong size =
java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
// Allocate the C++ Thread structure and create the native thread. The
// stack size retrieved from java is signed, but the constructor takes
// size_t (an unsigned type), so avoid passing negative values which would
// result in really large stacks.
size_t sz = size > 0 ? (size_t) size : 0;
native_thread = new JavaThread(&thread_entry, sz);
// At this point it may be possible that no osthread was created for the
// JavaThread due to lack of memory. Check for this situation and throw
// an exception if necessary. Eventually we may want to change this so
// that we only grab the lock if the thread was created successfully -
// then we can also do this check and throw the exception in the
// JavaThread constructor.
if (native_thread->osthread() != NULL) {
// Note: the current thread is not being used within "prepare".
native_thread->prepare(jthread);
}
}
}
if (throw_illegal_thread_state) {
THROW(vmSymbols::java_lang_IllegalThreadStateException());
}
assert(native_thread != NULL, "Starting null thread?");
if (native_thread->osthread() == NULL) {
// No one should hold a reference to the 'native_thread'.
delete native_thread;
if (JvmtiExport::should_post_resource_exhausted()) {
JvmtiExport::post_resource_exhausted(
JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS,
"unable to create new native thread");
}
THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(),
"unable to create new native thread");
}
// 跟踪这个方法 hotspot->thread.cpp
Thread::start(native_thread);
JVM_END
thread.cpp:
void Thread::start(Thread* thread) {
trace("start", thread);
// Start is different from resume in that its safety is guaranteed by context or
// being called from a Java method synchronized on the Thread object.
if (!DisableStartThread) {
if (thread->is_Java_thread()) {
// Initialize the thread state to RUNNABLE before starting this thread.
// Can not set it after the thread started because we do not know the
// exact thread state at that time. It could be in MONITOR_WAIT or
// in SLEEPING or some other state.
java_lang_Thread::set_thread_status(((JavaThread*)thread)->threadObj(),
java_lang_Thread::RUNNABLE);
}
// 跟踪hotspot os.cpp
os::start_thread(thread);
}
}
os.cpp:
void os::start_thread(Thread* thread) {
// guard suspend/resume
MutexLockerEx ml(thread->SR_lock(), Mutex::_no_safepoint_check_flag);
OSThread* osthread = thread->osthread();
osthread->set_state(RUNNABLE);
// 启动的方法
pd_start_thread(thread);
}
执行启动线程命令,则我们可以猜想下,再线程结束的时候是否会唤醒线程。跟踪到线程结束的地方(由于hotspot源码调用关系较多,省略跟踪过程)。最终调用的方法是hotspot\src\os\windows\vm\os_windows.cpp#os::pd_start_thread
,linux对应os_linux.cpp
// windows
void os::pd_start_thread(Thread* thread) {
// 调用操作系统回复线程命令唤醒
DWORD ret = ResumeThread(thread->osthread()->thread_handle());
// Returns previous suspend state:
// 0: Thread was not suspended
// 1: Thread is running now
// >1: Thread is still suspended.
assert(ret != SYS_THREAD_ERROR, "StartThread failed"); // should propagate back
}
//linux
void os::pd_start_thread(Thread* thread) {
OSThread * osthread = thread->osthread();
assert(osthread->get_state() != INITIALIZED, "just checking");
Monitor* sync_with_child = osthread->startThread_lock();
MutexLockerEx ml(sync_with_child, Mutex::_no_safepoint_check_flag);
// notify 调用操作系统命令唤醒线程
sync_with_child->notify();
}
可知join命令实质上是通过,wait/notify操作实现的线程等待。