线程生命周期
一、Java线程生命周期
- 新建状态(NEW)
- 线程已经被创建,但是还不准分配CPU进行运行。这个是在每一种编程状态特有的。就像在java中使用
new Thread()
创建一个线程对象。此时的线程就处于一个新建状态。这也就是在创建一个线程对象之后调用run
方法就和运行普通的方法一样的原因。本质上都是都在调用线程中调用对象方法。
- 线程已经被创建,但是还不准分配CPU进行运行。这个是在每一种编程状态特有的。就像在java中使用
- 可运行状态(RUNNABLE)
- 新创建的线程对象已经可以分配
CPU
,根据不同的调度算法只要有空闲的CPU
分配到时候,线程就可以真正的开始运行。此时的线程在已经在操作系统被创建,只要有空闲的CPU
分配过来线程就可以处于运行状态。在Java
中的调用线程的的start
方法一样在操作系统中创建对应的线程。
- 新创建的线程对象已经可以分配
- 阻塞状态(BLOCKED)
- 在
Java
中一个线程进入阻塞状态的只有这样的两个就是当线程进入使用synchronized
修饰的代码块或者方法的时候。等待持有锁的线程释放锁。当持有锁的线程释放锁之后,当前阻塞的线程获取到锁线程的状态会从blocked->runnable
。
- 在
- 无限期等待状态(WAITING)。
- 当一个线程从
Runnable -> waiting
存在如下的几种情况- 调用对象
wait()
方法。那要将一个线程从调用wait()
而变成无限期等待的状态变为可运行状态只需要调用Object.notify()
或则会Object.notifyAll()
- 在一个线程中调用另外一个线程的
join()
方法。 - 处于可运行状态的线程调用
LockSupport.park()
。线程的状态将会变为无限期等待。需要线程从无限期的等待中恢复调用LockSupport.unpark()
- 调用对象
- 当一个线程从
- 有限期等待状态(TIMED_WAITING)
- 线程从
Runnable -> timed_waiting
- 调用Thread.sleep(long)
- 调用Object.wait(long),状态恢复Object.notify()、Object.notifyAll()
- 调用Object.join(long)
- 调用LockSupport.parkNanos()。状态恢复LockSupport.unpark;
- 调用LockSupport.parkUntil();
- 线程从
- 终止状态(TERMINATED)
- 线程正常运行结束或者是线程由于异常退出线程都会处于终止状态。
- 线程状态转换示意图:
二、new thread().start发生了啥?
-
在java里面创建一个线程无非两种方式如下所示:
-
继承Thread重写run方法
public class ThreadStudy extends Thread{ @Override public void run() { super.run(); System.out.println("extends Thread"); } }
-
实现Runnable接口
public class RunnableStudy implements Runnable{ @Override public void run() { System.out.println("implements Runnable"); } }
-
调用实现
public class MainTest { public static void main(String[] args) { Thread t1 = new ThreadStudy(); Thread t2 = new Thread(new RunnableStudy()); t1.start(); t2.start(); } }
-
-
整个操作可以分为两步
-
执行
new Thread()
创建线程对象。在Thread
的java源码中存在如下的静态代码块private static native void registerNatives(); static { registerNatives(); }
从上面的代码可知
registerNatives
是一个native
的方法,由于是使用static
关键子修饰会随着类的初始化而首先加载,在jvm中的实现如下// todo 在java的Thread加载的时候初始化这个类中的方法映射 JNIEXPORT void JNICALL Java_java_lang_Thread_registerNatives(JNIEnv *env, jclass cls) { (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods)); } // methods是一个结构体数组,具体做的是将java中的native方法和jvm中的方法进行映射,数组如下 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}, {"interrupt0", "()V", (void *)&JVM_Interrupt}, {"holdsLock", "(" OBJ ")Z", (void *)&JVM_HoldsLock}, {"getThreads", "()[" THD, (void *)&JVM_GetAllThreads}, {"dumpThreads", "([" THD ")[[" STE, (void *)&JVM_DumpThreads}, {"setNativeName", "(" STR ")V", (void *)&JVM_SetNativeThreadName}, };
上述操作就是在执行
new Thread()
的时候执行操作,也相当于初始化操作。 -
当一个线程创建完成之后,就需要调用线程的
start()
方法启动线程,先看看在java中的start()
方法public synchronized void start() { /** * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW". */ if (threadStatus != 0) throw new IllegalThreadStateException(); /* Notify the group that this thread is about to be started * so that it can be added to the group's list of threads * and the group's unstarted count can be decremented. */ group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { /* do nothing. If start0 threw a Throwable then it will be passed up the call stack */ } } } private native void start0(); //从前面的代码可知最终是通过调用start0()方法实现的线程启动,而start0()方法是一个native方法,请看上面start0()正是前面做过映射方法
-
这个
start0()
方法映射到的方法是JVM_StartThread
需要看JVM_StartThread
这个方法的实现。我们在jvm.h
中找到这个方法/* * java.lang.Thread */ // 启动线程的接口 JNIEXPORT void JNICALL JVM_StartThread(JNIEnv *env, jobject thread); // 这个方法具体的实现在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 64-bit signed, but the constructor takes // size_t (an unsigned type), which may be 32 or 64-bit depending on the platform. // - Avoid truncating on 32-bit platforms if size is greater than UINT_MAX. // - Avoid passing negative values which would result in really large stacks. NOT_LP64(if (size > SIZE_MAX) size = SIZE_MAX;) size_t sz = size > 0 ? (size_t) size : 0; //构造thread对象 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'. native_thread->smr_delete(); if (JvmtiExport::should_post_resource_exhausted()) { JvmtiExport::post_resource_exhausted( JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS, os::native_thread_creation_failed_msg()); } THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(), os::native_thread_creation_failed_msg()); } #if INCLUDE_JFR if (Jfr::is_recording() && EventThreadStart::is_enabled() && EventThreadStart::is_stacktrace_enabled()) { JfrThreadLocal* tl = native_thread->jfr_thread_local(); // skip Thread.start() and Thread.start0() tl->set_cached_stack_trace_id(JfrStackTraceRepository::record(thread, 2)); } #endif //启动Thread线程 Thread::start(native_thread); JVM_END
-
在
jvm.cpp
中的实现中使用new JavaThread(&thread_entry, sz)
创建了一个线程实例,那在创建的时候到底做了啥呢?请继续向下看//这个在thread.c。这个是其构造函数。在构造函数中可以看到最终是通过os的create_thread创建的线程 JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) : JavaThread() { _jni_attach_state = _not_attaching_via_jni; set_entry_point(entry_point); // Create the native thread itself. // %note runtime_23 os::ThreadType thr_type = os::java_thread; thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread : os::java_thread; os::create_thread(this, thr_type, stack_sz); // The _osthread may be NULL here because we ran out of memory (too many threads active). // We need to throw and OutOfMemoryError - however we cannot do this here because the caller // may hold a lock and all locks must be unlocked before throwing the exception (throwing // the exception consists of creating the exception object & initializing it, initialization // will leave the VM via a JavaCall and then all locks must be unlocked). // // The thread is still suspended when we reach here. Thread must be explicit started // by creator! Furthermore, the thread must also explicitly be added to the Threads list // by calling Threads:add. The reason why this is not done here, is because the thread // object must be fully initialized (take a look at JVM_Start) }
-
os
是对不同平台的抽象,其具体的实现有linux,bsd,windows
等这些。在生产实践中linux
使用最多。那我们就看看在linux
上的实现。在os_linux.cpp
中对应的方法如下// 在操作系统中创建thread bool os::create_thread(Thread* thread, ThreadType thr_type, size_t req_stack_size) { assert(thread->osthread() == NULL, "caller responsible"); // Allocate the OSThread object OSThread* osthread = new OSThread(NULL, NULL); if (osthread == NULL) { return false; } // set the correct thread state osthread->set_thread_type(thr_type); // Initial state is ALLOCATED but not INITIALIZED osthread->set_state(ALLOCATED); thread->set_osthread(osthread); // init thread attributes pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); // Calculate stack size if it's not specified by caller. size_t stack_size = os::Posix::get_initial_stack_size(thr_type, req_stack_size); // In glibc versions prior to 2.7 the guard size mechanism // is not implemented properly. The posix standard requires adding // the size of the guard pages to the stack size, instead Linux // takes the space out of 'stacksize'. Thus we adapt the requested // stack_size by the size of the guard pages to mimick proper // behaviour. However, be careful not to end up with a size // of zero due to overflow. Don't add the guard page in that case. size_t guard_size = os::Linux::default_guard_size(thr_type); // Configure glibc guard page. Must happen before calling // get_static_tls_area_size(), which uses the guard_size. pthread_attr_setguardsize(&attr, guard_size); size_t stack_adjust_size = 0; if (AdjustStackSizeForTLS) { // Adjust the stack_size for on-stack TLS - see get_static_tls_area_size(). stack_adjust_size += get_static_tls_area_size(&attr); } else { stack_adjust_size += guard_size; } stack_adjust_size = align_up(stack_adjust_size, os::vm_page_size()); if (stack_size <= SIZE_MAX - stack_adjust_size) { stack_size += stack_adjust_size; } assert(is_aligned(stack_size, os::vm_page_size()), "stack_size not aligned"); int status = pthread_attr_setstacksize(&attr, stack_size); if (status != 0) { // pthread_attr_setstacksize() function can fail // if the stack size exceeds a system-imposed limit. assert_status(status == EINVAL, status, "pthread_attr_setstacksize"); log_warning(os, thread)("The %sthread stack size specified is invalid: " SIZE_FORMAT "k", (thr_type == compiler_thread) ? "compiler " : ((thr_type == java_thread) ? "" : "VM "), stack_size / K); thread->set_osthread(NULL); delete osthread; return false; } ThreadState state; { pthread_t tid; //通过Linux提供的pthrad创建线程 int ret = pthread_create(&tid, &attr, (void* (*)(void*)) thread_native_entry, thread); char buf[64]; if (ret == 0) { log_info(os, thread)("Thread started (pthread id: " UINTX_FORMAT ", attributes: %s). ", (uintx) tid, os::Posix::describe_pthread_attr(buf, sizeof(buf), &attr)); } else { log_warning(os, thread)("Failed to start thread - pthread_create failed (%s) for attributes: %s.", os::errno_name(ret), os::Posix::describe_pthread_attr(buf, sizeof(buf), &attr)); // Log some OS information which might explain why creating the thread failed. log_info(os, thread)("Number of threads approx. running in the VM: %d", Threads::number_of_threads()); LogStream st(Log(os, thread)::info()); os::Posix::print_rlimit_info(&st); os::print_memory_info(&st); os::Linux::print_proc_sys_info(&st); os::Linux::print_container_info(&st); } pthread_attr_destroy(&attr); if (ret != 0) { // Need to clean up stuff we've allocated so far thread->set_osthread(NULL); delete osthread; return false; } // Store pthread info into the OSThread osthread->set_pthread_id(tid); // Wait until child thread is either initialized or aborted { Monitor* sync_with_child = osthread->startThread_lock(); MutexLocker ml(sync_with_child, Mutex::_no_safepoint_check_flag); while ((state = osthread->get_state()) == ALLOCATED) { sync_with_child->wait_without_safepoint_check(); } } } // The thread is returned suspended (in state INITIALIZED), // and is started higher up in the call chain assert(state == INITIALIZED, "race condition"); return true; }
从上述的代码可知是通过
int ret = pthread_create(&tid, &attr, (void* (*)(void*)) thread_native_entry, thread);
这个语句来床架一个操作系统级别的线程。至于在操作系统上是如何实现线程的创建,奈何小弟太菜没发继续往下看。到此在jjvm
层面创建一个线程就已经完成。 -
在线程创建完成之后,在
jvm.cpp
中后续使用Thread::start(native_thread);
来启动线程,具体的源码如下// 在thread.cpp中启动的方法如下 void Thread::start(Thread* 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(thread->as_Java_thread()->threadObj(), JavaThreadStatus::RUNNABLE); } os::start_thread(thread); } } // 从上述的代码可知最终是通过调用os的start_thread方法实现线程的启动,os中启动线程的方法如下 // The INITIALIZED state is distinguished from the SUSPENDED state because the // conditions in which a thread is first started are different from those in which // a suspension is resumed. These differences make it hard for us to apply the // tougher checks when starting threads that we want to do when resuming them. // However, when start_thread is called as a result of Thread.start, on a Java // thread, the operation is synchronized on the Java Thread object. So there // cannot be a race to start the thread and hence for the thread to exit while // we are working on it. Non-Java threads that start Java threads either have // to do so in a context in which races are impossible, or should do appropriate // locking. void os::start_thread(Thread* thread) { // guard suspend/resume MutexLocker ml(thread->SR_lock(), Mutex::_no_safepoint_check_flag); OSThread* osthread = thread->osthread(); osthread->set_state(RUNNABLE); pd_start_thread(thread); } // 最终是通过调用pd_start_thread(thread)方法实现线程的启动,在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(); MutexLocker ml(sync_with_child, Mutex::_no_safepoint_check_flag); //通知前面创建的线程开始运行 sync_with_child->notify(); }
到此为止在java中创建一个线程主流程就完了,但是还有很多的细节等待去补充。
-