大家好,我是小滿,最近一直在梳理Java并發(fā),但內(nèi)容雜且偏晦澀,今天我們一起來(lái)聊聊Java 線程的狀態(tài)及轉(zhuǎn)換 先來(lái)夯實(shí)一下基礎(chǔ),萬(wàn)丈高樓平地起,路還是得慢慢走。
Java線程的生命周期
我們先來(lái)看下Java線程的生命周期圖:

上圖也是本文的大綱,我們下面依次聊聊java各個(gè)線程狀態(tài)及其他們的轉(zhuǎn)換。
線程初始狀態(tài)
線程初始狀態(tài)(NEW): 當(dāng)前線程處于線程被創(chuàng)建出來(lái)但沒(méi)有被調(diào)用start()
在Java線程的時(shí)間中,關(guān)于線程的一切的起點(diǎn)是從Thread 類的對(duì)象的創(chuàng)建開(kāi)始,一般實(shí)現(xiàn)Runnable接口 或者 繼承Thread類的類,實(shí)例化一個(gè)對(duì)象出來(lái),線程就進(jìn)入了初始狀態(tài)
Thread thread = new Thread()
由于線程在我們操作系統(tǒng)中也是非常寶貴的資源,在實(shí)際開(kāi)發(fā)中,我們常常用線程池來(lái)重復(fù)利用現(xiàn)有的線程來(lái)執(zhí)行任務(wù),避免多次創(chuàng)建和銷毀線程,從而降低創(chuàng)建和銷毀線程過(guò)程中的代價(jià)。Java 給我們提供了 Executor 接口來(lái)使用線程池,查看其JDK1.8源碼,發(fā)現(xiàn)其內(nèi)部封裝了Thread t = new Thread()
public class Executors {
? ? ...
? static class DefaultThreadFactory implements ThreadFactory {
? ? ? ? private static final AtomicInteger poolNumber = new AtomicInteger(1);
? ? ? ? private final ThreadGroup group;
? ? ? ? private final AtomicInteger threadNumber = new AtomicInteger(1);
? ? ? ? private final String namePrefix;
? ? ? ? ...
? ? ? ? public Thread newThread(Runnable r) {
? ? ? ? ? ? Thread t = new Thread(group, r,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? namePrefix + threadNumber.getAndIncrement(),
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 0);
? ? ? ? ? ? if (t.isDaemon())
? ? ? ? ? ? ? ? t.setDaemon(false);
? ? ? ? ? ? if (t.getPriority() != Thread.NORM_PRIORITY)
? ? ? ? ? ? ? ? t.setPriority(Thread.NORM_PRIORITY);
? ? ? ? ? ? return t;
? ? ? ? }
? ? }
? ? ...
}
在thread類源碼中,我們還能發(fā)現(xiàn)線程狀態(tài)的枚舉類State
public enum State {
? ? ? ? /**
? ? ? ? * Thread state for a thread which has not yet started.
? ? ? ? */
? ? ? ? NEW,
? ? ? ? RUNNABLE,
? ? ? ? BLOCKED,
? ? ? ? WAITING,
? ? ? ? TIMED_WAITING,
? ? ? ? /**
? ? ? ? * Thread state for a terminated thread.
? ? ? ? * The thread has completed execution.
? ? ? ? */
? ? ? ? TERMINATED;
? ? }
所謂線程的狀態(tài),在java源碼中都是通過(guò)threadStatus的值來(lái)表示的
/* Java thread status for tools,
? ? * initialized to indicate thread 'not yet started'
? ? */
? ? private volatile int threadStatus = 0;
State?和?threadStatus?通過(guò)toThreadState方法映射轉(zhuǎn)換
public State getState() {
? ? ? ? // get current thread state
? ? ? ? return sun.misc.VM.toThreadState(threadStatus);
? ? }
//--- --- ---
? ? public static State toThreadState(int var0) {
? ? ? ? if ((var0 & 4) != 0) {
? ? ? ? ? ? return State.RUNNABLE;
? ? ? ? } else if ((var0 & 1024) != 0) {
? ? ? ? ? ? return State.BLOCKED;
? ? ? ? } else if ((var0 & 16) != 0) {
? ? ? ? ? ? return State.WAITING;
? ? ? ? } else if ((var0 & 32) != 0) {
? ? ? ? ? ? return State.TIMED_WAITING;
? ? ? ? } else if ((var0 & 2) != 0) {
? ? ? ? ? ? return State.TERMINATED;
? ? ? ? } else {
? ? ? ? ? ? return (var0 & 1) == 0 ? State.NEW : State.RUNNABLE;
? ? ? ? }
? ? }
到這里我們就可以發(fā)現(xiàn),Thread t = new Thread()在Java中只是設(shè)置了線程的狀態(tài),操作系統(tǒng)中并沒(méi)有的實(shí)際線程的創(chuàng)建
線程運(yùn)行狀態(tài)
線程運(yùn)行狀態(tài)(RUNNABLE),線程被調(diào)用了start()等待運(yùn)行的狀態(tài)
在Linux操作系統(tǒng)層面,包含Running和?Ready?狀態(tài)。其中Ready狀態(tài)是等待 CPU 時(shí)間片?,F(xiàn)今主流的JVM,比如hotspot虛擬機(jī)都是把Java 線程,映射到操作系統(tǒng)OS底層的線程上,把調(diào)度委托給了操作系統(tǒng)。而操作系統(tǒng)比如Linux,它是多任務(wù)操作系統(tǒng),充分利用CPU的高性能,將CPU的時(shí)間分片,讓單個(gè)CPU實(shí)現(xiàn)"同時(shí)執(zhí)行"多任務(wù)的效果。
Linux的任務(wù)調(diào)度又采用搶占式輪轉(zhuǎn)調(diào)度,我們不考慮特權(quán)進(jìn)程的話,OS會(huì)選擇在CPU上占用的時(shí)間最少進(jìn)程,優(yōu)先在cpu上分配資源,其對(duì)應(yīng)的線程去執(zhí)行任務(wù),盡可能地維護(hù)任務(wù)調(diào)度公平。Running和?Ready?狀態(tài)的線程在CPU中切換狀態(tài)非常短暫。大概只有 0.01 秒這一量級(jí),區(qū)分開(kāi)來(lái)意義不大,java將這2個(gè)狀態(tài)統(tǒng)一用RUNNABLE來(lái)表示
thread.start()源碼解析
我們接下來(lái)看看為什么說(shuō)執(zhí)行thread.start()后,線程的才"真正的創(chuàng)建"
public class ThreadTest {
? ? /**
? ? * 繼承Thread類
? ? */
? ? public static class MyThread extends Thread {
? ? ? ? @Override
? ? ? ? public void run() {
? ? ? ? ? ? System.out.println("This is child thread");
? ? ? ? }
? ? }
? ? public static void main(String[] args) {
? ? ? ? MyThread thread = new MyThread();
? ? ? ? thread.start();
? ? }
}
其中thread.start()方法的源碼中,會(huì)去調(diào)用start0()方法,而start0()是private native void start0();JVM調(diào)用Native方法的話,會(huì)進(jìn)入到不受JVM控制的世界里
在Thread類實(shí)例化的同時(shí),會(huì)首先調(diào)用registerNatives方法,注冊(cè)本地Native方法,動(dòng)態(tài)綁定JVM方法
private static native void registerNatives();
? ? static {
? ? ? ? registerNatives();
? ? }
在Thread類中通過(guò)registerNatives將指定的本地方法綁定到指定函數(shù),比如start0本地方法綁定到JVM_StartThread函數(shù):
...
static JNINativeMethod methods[] = {
? ? {"start0",? ? ? ? ? "()V",? ? ? ? (void *)&JVM_StartThread},
? ? {"stop0",? ? ? ? ? ? "(" OBJ ")V", (void *)&JVM_StopThread},
? ? {"isAlive",? ? ? ? ? "()Z",? ? ? ? (void *)&JVM_IsThreadAlive},
? ? ...
JVM_StartThread?是JVM層函數(shù),拋去各種情況的處理,主要是通過(guò)?new JavaThread(&thread_entry, sz)來(lái)創(chuàng)建JVM線程對(duì)象
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))? JVMWrapper("JVM_StartThread");? JavaThread *native_thread = NULL;//表示是否有異常,當(dāng)拋出異常時(shí)需要獲取Heap_lock。boolthrow_illegal_thread_state=false;// 在發(fā)布jvmti事件之前,必須釋放Threads_lock// in Thread::start.{// 獲取 Threads_lock鎖MutexLockermu(Threads_lock);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 runningjlongsize=java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));// 創(chuàng)建JVM線程(用JavaThread對(duì)象表示)size_tsz=size >0? (size_t) size :0;? ? ? native_thread =newJavaThread(&thread_entry, sz);? ? ? ...? ? }? }? ...? Thread::start(native_thread);//啟動(dòng)內(nèi)核線程JVM_END
源碼見(jiàn):https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/share/vm/prims/jvm.cpp
我們?cè)賮?lái)看看JavaThread的實(shí)現(xiàn),發(fā)現(xiàn)內(nèi)部通過(guò)?os::create_thread(this, thr_type, stack_sz);來(lái)調(diào)用不同操作系統(tǒng)的創(chuàng)建線程方法創(chuàng)建線程。
JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :? Thread()#ifINCLUDE_ALL_GCS? , _satb_mark_queue(&_satb_mark_queue_set),? _dirty_card_queue(&_dirty_card_queue_set)#endif// INCLUDE_ALL_GCS{if(TraceThreadEvents) {? ? tty->print_cr("creating thread %p",this);? }? initialize();? _jni_attach_state = _not_attaching_via_jni;? set_entry_point(entry_point);// Create the native thread itself.// %note runtime_23os::ThreadTypethr_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);//調(diào)用不同操作系統(tǒng)的創(chuàng)建線程方法創(chuàng)建線程}
源碼見(jiàn):https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/share/vm/runtime/thread.cpp
我們都知道Java是跨平臺(tái)的,但是native各種方法底層c/c++代碼對(duì)各平臺(tái)都需要有對(duì)應(yīng)的兼容,我們這邊以linux為例,其他平臺(tái)就大家自行去查閱了
bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {assert(thread->osthread() == NULL,"caller responsible");// Allocate the OSThread objectOSThread* osthread =newOSThread(NULL, NULL);if(osthread == NULL) {returnfalse;? }// set the correct thread stateosthread->set_thread_type(thr_type);// Initial state is ALLOCATED but not INITIALIZEDosthread->set_state(ALLOCATED);? thread->set_osthread(osthread);// init thread attributespthread_attr_t attr;? pthread_attr_init(&attr);? pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);// stack sizeif(os::Linux::supports_variable_stack_size()) {// calculate stack size if it's not specified by callerif(stack_size ==0) {? ? ? stack_size = os::Linux::default_stack_size(thr_type);switch(thr_type) {caseos::java_thread:// Java threads use ThreadStackSize which default value can be// changed with the flag -Xssassert(JavaThread::stack_size_at_create() >0,"this should be set");? ? ? ? stack_size = JavaThread::stack_size_at_create();break;caseos::compiler_thread:if(CompilerThreadStackSize >0) {? ? ? ? ? stack_size = (size_t)(CompilerThreadStackSize * K);break;? ? ? ? }// else fall through:// use VMThreadStackSize if CompilerThreadStackSize is not definedcaseos::vm_thread:caseos::pgc_thread:caseos::cgc_thread:caseos::watcher_thread:if(VMThreadStackSize >0) stack_size = (size_t)(VMThreadStackSize * K);break;? ? ? }? ? }? ? stack_size = MAX2(stack_size, os::Linux::min_stack_allowed);? ? pthread_attr_setstacksize(&attr, stack_size);? }else{// let pthread_create() pick the default value.}// glibc guard pagepthread_attr_setguardsize(&attr, os::Linux::default_guard_size(thr_type));? ThreadState state;? {// Serialize thread creation if we are running with fixed stack LinuxThreadsboollock=os::Linux::is_LinuxThreads() && !os::Linux::is_floating_stack();if(lock) {? ? ? os::Linux::createThread_lock()->lock_without_safepoint_check();? ? }? ? pthread_t tid;//通過(guò)pthread_create方法創(chuàng)建內(nèi)核級(jí)線程 !intret=pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);? ? pthread_attr_destroy(&attr);if(ret !=0) {if(PrintMiscellaneous && (Verbose || WizardMode)) {? ? ? ? perror("pthread_create()");? ? ? }// Need to clean up stuff we've allocated so farthread->set_osthread(NULL);? ? ? delete osthread;if(lock) os::Linux::createThread_lock()->unlock();returnfalse;? ? }// Store pthread info into the OSThreadosthread->set_pthread_id(tid);// Wait until child thread is either initialized or aborted{? ? ? Monitor* sync_with_child = osthread->startThread_lock();? ? ? MutexLockerExml(sync_with_child, Mutex::_no_safepoint_check_flag);while((state = osthread->get_state()) == ALLOCATED) {? ? ? ? sync_with_child->wait(Mutex::_no_safepoint_check_flag);? ? ? }? ? }if(lock) {? ? ? os::Linux::createThread_lock()->unlock();? ? }? }// Aborted due to thread limit being reachedif(state == ZOMBIE) {? ? ? thread->set_osthread(NULL);? ? ? delete osthread;returnfalse;? }// The thread is returned suspended (in state INITIALIZED),// and is started higher up in the call chainassert(state == INITIALIZED,"race condition");returntrue;}
源碼見(jiàn):https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/os/linux/vm/os_linux.cpp
主要通過(guò)pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread),它是unix 創(chuàng)建線程的方法,linux也繼承了。調(diào)用后在linux系統(tǒng)中會(huì)創(chuàng)建一個(gè)內(nèi)核級(jí)的線程。也就是說(shuō)這個(gè)時(shí)候操作系統(tǒng)中線程才真正地誕生
更多精彩文章在公眾號(hào)「小牛呼嚕嚕」
但此時(shí)線程才誕生,那是怎么啟動(dòng)的?我們回到JVM_StartThread源碼中,Thread::start(native_thread)很明顯這行代碼就表示啟動(dòng)native_thread = new JavaThread(&thread_entry, sz)創(chuàng)建的線程,我們來(lái)繼續(xù)看看其源碼
voidThread::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()) {// 設(shè)置線程狀態(tài)java_lang_Thread::set_thread_status(((JavaThread*)thread)->threadObj(),? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? java_lang_Thread::RUNNABLE);? ? }? ? os::start_thread(thread);? }}
源碼:https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/share/vm/runtime/thread.cpp
os::start_thread它封裝了pd_start_thread(thread),執(zhí)行該方法,操作系統(tǒng)會(huì)去啟動(dòng)指定的線程
voidos::start_thread(Thread* thread) {// guard suspend/resumeMutexLockerExml(thread->SR_lock(), Mutex::_no_safepoint_check_flag);? OSThread* osthread = thread->osthread();? osthread->set_state(RUNNABLE);? pd_start_thread(thread);}
當(dāng)操作系統(tǒng)的線程啟動(dòng)完之后,我們?cè)倩氐絧thread_create(&tid, &attr, (void* (*)(void*)) java_start, thread),會(huì)去java_start這個(gè)線程入口函數(shù)進(jìn)行OS內(nèi)核級(jí)線程的初始化,并開(kāi)始啟動(dòng)JavaThread
// Thread start routine for all newly created threadsstaticvoid*java_start(Thread *thread) {// Try to randomize the cache line index of hot stack frames.// This helps when threads of the same stack traces evict each other's// cache lines. The threads can be either from the same JVM instance, or// from different JVM instances. The benefit is especially true for// processors with hyperthreading technology.staticintcounter=0;intpid=os::current_process_id();? alloca(((pid ^ counter++) &7) *128);? ThreadLocalStorage::set_thread(thread);? OSThread* osthread = thread->osthread();? Monitor* sync = osthread->startThread_lock();// non floating stack LinuxThreads needs extra check, see aboveif(!_thread_safety_check(thread)) {// notify parent threadMutexLockerExml(sync, Mutex::_no_safepoint_check_flag);? ? osthread->set_state(ZOMBIE);? ? sync->notify_all();returnNULL;? }// thread_id is kernel thread id (similar to Solaris LWP id)osthread->set_thread_id(os::Linux::gettid());if(UseNUMA) {intlgrp_id=os::numa_get_group_id();if(lgrp_id != -1) {? ? ? thread->set_lgrp_id(lgrp_id);? ? }? }// initialize signal mask for this threados::Linux::hotspot_sigmask(thread);// initialize floating point control registeros::Linux::init_thread_fpu_state();// handshaking with parent thread{? ? MutexLockerExml(sync, Mutex::_no_safepoint_check_flag);// notify parent threadosthread->set_state(INITIALIZED);? ? sync->notify_all();// 等待,直到操作系統(tǒng)級(jí)線程全部啟動(dòng)while(osthread->get_state() == INITIALIZED) {? ? ? sync->wait(Mutex::_no_safepoint_check_flag);? ? }? }// 開(kāi)始運(yùn)行JavaThread::runthread->run();return0;}
源碼:https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/os/linux/vm/os_linux.cpp
thread->run()其實(shí)就是JavaThread::run()也表明方法開(kāi)始回調(diào),從OS層方法回到JVM層方法
,我們?cè)賮?lái)看下其實(shí)現(xiàn):
// The first routine called by a new Java thread
void JavaThread::run() {
? // initialize thread-local alloc buffer related fields
? this->initialize_tlab();
? // used to test validitity of stack trace backs
? this->record_base_of_stack_pointer();
? // Record real stack base and size.
? this->record_stack_base_and_size();
? // Initialize thread local storage; set before calling MutexLocker
? this->initialize_thread_local_storage();
? this->create_stack_guard_pages();
? this->cache_global_variables();
? // Thread is now sufficient initialized to be handled by the safepoint code as being
? // in the VM. Change thread state from _thread_new to _thread_in_vm
? ThreadStateTransition::transition_and_fence(this, _thread_new, _thread_in_vm);
? assert(JavaThread::current() == this, "sanity check");
? assert(!Thread::current()->owns_locks(), "sanity check");
? DTRACE_THREAD_PROBE(start, this);
? // This operation might block. We call that after all safepoint checks for a new thread has
? // been completed.
? this->set_active_handles(JNIHandleBlock::allocate_block());
? if (JvmtiExport::should_post_thread_life()) {
? ? JvmtiExport::post_thread_start(this);
? }
? JFR_ONLY(Jfr::on_thread_start(this);)
? // We call another function to do the rest so we are sure that the stack addresses used
? // from there will be lower than the stack base just computed
? thread_main_inner();//!!!注意此處方法
? // Note, thread is no longer valid at this point!
}
void JavaThread::thread_main_inner() {
? assert(JavaThread::current() == this, "sanity check");
? assert(this->threadObj() != NULL, "just checking");
? // Execute thread entry point unless this thread has a pending exception
? // or has been stopped before starting.
? // Note: Due to JVM_StopThread we can have pending exceptions already!
? if (!this->has_pending_exception() &&
? ? ? !java_lang_Thread::is_stillborn(this->threadObj())) {
? ? {
? ? ? ResourceMark rm(this);
? ? ? this->set_native_thread_name(this->get_thread_name());
? ? }
? ? HandleMark hm(this);
? ? this->entry_point()(this, this);//JavaThread對(duì)象中傳入的entry_point為Thread對(duì)象的Thread::run方法
? }
? DTRACE_THREAD_PROBE(stop, this);
? this->exit(false);
? delete this;
}
源碼:https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/share/vm/runtime/thread.cpp
由于JavaThread定義可知JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz)中參數(shù)entry_point是外部傳入,那我們想想JavaThread是什么時(shí)候?qū)嵗模?/p>
沒(méi)錯(cuò),就是我們一開(kāi)始的JVM_StartThread中native_thread = new JavaThread(&thread_entry, sz);
也就是說(shuō)this->entry_point()(this, this)實(shí)際上是回調(diào)的thread_entry方法
thread_entry源碼:
staticvoidthread_entry(JavaThread* thread, TRAPS){? HandleMarkhm(THREAD);? Handleobj(THREAD, thread->threadObj());? JavaValueresult(T_VOID);? JavaCalls::call_virtual(&result,? ? ? ? ? ? ? ? ? ? ? ? ? obj,? ? ? ? ? ? ? ? ? ? ? ? ? KlassHandle(THREAD, SystemDictionary::Thread_klass()),? ? ? ? ? ? ? ? ? ? ? ? ? vmSymbols::run_method_name(),? ? ? ? ? ? ? ? ? ? ? ? ? vmSymbols::void_method_signature(),? ? ? ? ? ? ? ? ? ? ? ? ? THREAD);}
源碼:https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/share/vm/prims/jvm.cpp
通過(guò)JavaCalls::call_virtual方法,又從JVM層 回到了Java語(yǔ)言層 ,即MyThread thread = new MyThread(); thread.start();
一切又回到了起點(diǎn),這就是Javathread.start()內(nèi)部完整的一個(gè)流程,HotSpot虛擬機(jī)實(shí)現(xiàn)的Java線程其實(shí)是對(duì)Linux內(nèi)核級(jí)線程的直接映射,將Java涉及到的所有線程調(diào)度、內(nèi)存分配都交由操作系統(tǒng)進(jìn)行管理。
線程終止?fàn)顟B(tài)
線程終止?fàn)顟B(tài)(TERMINATED),表示該線程已經(jīng)運(yùn)行完畢。
當(dāng)一個(gè)線程執(zhí)行完畢,或者主線程的main()方法完成時(shí),我們就認(rèn)為它終止了。終止的線程無(wú)法在被使用,如果調(diào)用start()方法,會(huì)拋出java.lang.IllegalThreadStateException異常,這一點(diǎn)我們可以從start源碼中很容易地得到
publicsynchronizedvoidstart(){if(threadStatus !=0)thrownewIllegalThreadStateException();? ? ...}
線程阻塞狀態(tài)
線程阻塞狀態(tài)(BLOCKED),需要等待鎖釋放或者說(shuō)獲取鎖失敗時(shí),線程阻塞
publicclassBlockedThreadimplementsRunnable{@Overridepublicvoidrun(){synchronized(BlockedThread.class){while(true){? ? ? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? }? ? }}
從Thread源碼的注釋中,我們可以知道等待鎖釋放或者說(shuō)獲取鎖失敗,主要有下面3中情況:
進(jìn)入 synchronized 方法時(shí)
進(jìn)入 synchronized 塊時(shí)
調(diào)用 wait 后, 重新進(jìn)入 synchronized 方法/塊時(shí)
其中第三種情況,大家可以先思考一下,我們留在下文線程等待狀態(tài)再詳細(xì)展開(kāi)
線程等待狀態(tài)
線程等待狀態(tài)(WAITING),表示該線程需要等待其他線程做出一些特定動(dòng)作(通知或中斷)。
wait/notify/notifyAll
我們緊接著上一小節(jié),調(diào)用?wait 后, 重新進(jìn)入synchronized 方法/塊時(shí),我們來(lái)看看期間發(fā)生了什么?
當(dāng)線程1調(diào)用對(duì)象A的wait方法后,會(huì)釋放當(dāng)前的鎖,然后讓出CPU時(shí)間片,線程會(huì)進(jìn)入該對(duì)象的等待隊(duì)列中,線程狀態(tài)變?yōu)?等待狀態(tài)WAITING。
當(dāng)另一個(gè)線程2調(diào)用了對(duì)象A的notify()/notifyAll()方法
notify()方法只會(huì)喚醒沉睡的線程,不會(huì)立即釋放之前占有的對(duì)象A的鎖,必須執(zhí)行完notify()方法所在的synchronized代碼塊后才釋放。所以在編程中,盡量在使用了notify/notifyAll()后立即退出臨界區(qū)
線程1收到通知后退出等待隊(duì)列,并進(jìn)入線程運(yùn)行狀態(tài)RUNNABLE,等待 CPU 時(shí)間片分配, 進(jìn)而執(zhí)行后續(xù)操作,接著線程1重新進(jìn)入 synchronized 方法/塊時(shí),競(jìng)爭(zhēng)不到鎖,線程狀態(tài)變?yōu)榫€程阻塞狀態(tài)BLOCKED。如果競(jìng)爭(zhēng)到鎖,就直接接著運(yùn)行。線程等待狀態(tài) 切換到線程阻塞狀態(tài),無(wú)法直接切換,需要經(jīng)過(guò)線程運(yùn)行狀態(tài)。
我們?cè)賮?lái)看一個(gè)例子,鞏固鞏固:
publicclassWaitNotifyTest{publicstaticvoidmain(String[] args){ObjectA=newObject();newThread(newRunnable() {@Overridepublicvoidrun(){? ? ? ? ? ? ? ? System.out.println("線程1等待獲取 對(duì)象A的鎖...");synchronized(A) {try{? ? ? ? ? ? ? ? ? ? ? ? System.out.println("線程1獲取了 對(duì)象A的鎖");? ? ? ? ? ? ? ? ? ? ? ? Thread.sleep(3000);? ? ? ? ? ? ? ? ? ? ? ? System.out.println("線程1開(kāi)始運(yùn)行wait()方法進(jìn)行等待,進(jìn)入到等待隊(duì)列......");? ? ? ? ? ? ? ? ? ? ? ? A.wait();? ? ? ? ? ? ? ? ? ? ? ? System.out.println("線程1等待結(jié)束");? ? ? ? ? ? ? ? ? ? }catch(InterruptedException e) {? ? ? ? ? ? ? ? ? ? ? ? e.printStackTrace();? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? }).start();newThread(newRunnable() {@Overridepublicvoidrun(){? ? ? ? ? ? ? ? System.out.println("線程2等待獲取 對(duì)象A的鎖...");synchronized(A) {? ? ? ? ? ? ? ? ? ? System.out.println("線程2獲取了 對(duì)象A的鎖");try{? ? ? ? ? ? ? ? ? ? ? ? Thread.sleep(3000);? ? ? ? ? ? ? ? ? ? }catch(InterruptedException e) {? ? ? ? ? ? ? ? ? ? ? ? e.printStackTrace();? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? System.out.println("線程2將要運(yùn)行notify()方法進(jìn)行喚醒線程1");? ? ? ? ? ? ? ? ? ? A.notify();? ? ? ? ? ? ? ? }? ? ? ? ? ? }? ? ? ? }).start();? ? }}
結(jié)果:
線程1等待獲取 對(duì)象A的鎖...線程1獲取了 對(duì)象A的鎖線程2等待獲取 對(duì)象A的鎖...線程1開(kāi)始運(yùn)行wait()方法進(jìn)行等待,進(jìn)入到等待隊(duì)列......線程2獲取了 對(duì)象A的鎖線程2將要運(yùn)行notify()方法進(jìn)行喚醒線程1線程1等待結(jié)束
需要注意的是,wait/notify/notifyAll 只能在synchronized修飾的方法、塊中使用,?notify 是只隨機(jī)喚醒一個(gè)線程,而 notifyAll 是喚醒所有等待隊(duì)列中的線程
join
Thread類中的join方法的主要作用能讓線程之間的并行執(zhí)行變?yōu)榇袌?zhí)行,當(dāng)前線程等該加入該線程后面,等待該線程終止
publicstaticvoidmain(String[] args){Threadthread=newThread();? thread.start();? thread.join();? ...}
上面一個(gè)例子表示,程序在main主線程中調(diào)用thread線程的join方法,意味著main線程放棄CPU時(shí)間片(主線程會(huì)變成 WAITING 狀態(tài)),并返回thread線程,繼續(xù)執(zhí)行直到線程thread執(zhí)行完畢,換句話說(shuō)在主線程執(zhí)行過(guò)程中,插入thread線程,還得等thread線程執(zhí)行完后,才輪到主線程繼續(xù)執(zhí)行
如果查看JDKthread.join()底層實(shí)現(xiàn),會(huì)發(fā)現(xiàn)其實(shí)內(nèi)部封裝了wait(),notifyAll()
park/unpark
LockSupport.park() 掛起當(dāng)前線程;LockSupport.unpark(暫停線程對(duì)象) 恢復(fù)某個(gè)線程
packagecom.zj.ideaprojects.demo.test3;importjava.util.concurrent.Executors;importjava.util.concurrent.locks.LockSupport;publicclassThreadLockSupportTest{publicstaticvoidmain(String[] args)throwsInterruptedException {Threadthread=newThread(() -> {? ? ? ? ? ? System.out.println("start.....");try{? ? ? ? ? ? ? ? Thread.sleep(1000);? ? ? ? ? ? }catch(InterruptedException e) {? ? ? ? ? ? ? ? e.printStackTrace();? ? ? ? ? ? }? ? ? ? ? ? System.out.println("park....");? ? ? ? ? ? LockSupport.park();? ? ? ? ? ? System.out.println("resume.....");? ? ? ? });? ? ? ? thread.start();? ? ? ? Thread.sleep(3000);? ? ? ? System.out.println("unpark....");? ? ? ? LockSupport.unpark(thread);? ? }}
結(jié)果:
start.....
park....
unpark....
resume.....
當(dāng)程序調(diào)用LockSupport.park(),會(huì)讓當(dāng)前線程A的線程狀態(tài)會(huì)從 RUNNABLE 變成 WAITING,然后main主線程調(diào)用LockSupport.unpark(thread),讓指定的線程即線程A,從 WAITING 回到 RUNNABLE 。我們可以發(fā)現(xiàn)
park/unpark和wait/notify/notifyAll很像,但是他們有以下的區(qū)別:
wait,notify 和 notifyAll 必須事先獲取對(duì)象鎖,而 unpark 不必
park、unpark 可以先 unpark ,而 wait、notify 不能先 notify,必須先wait
unpark 可以精準(zhǔn)喚醒某一個(gè)確定的線程。而 notify 只能隨機(jī)喚醒一個(gè)等待線程,notifyAll 是喚醒所以等待線程,就不那么精確
超時(shí)等待狀態(tài)
超時(shí)等待狀態(tài)(TIMED_WAITING),也叫限期等待,可以在指定的時(shí)間后自行返回而不是像 WAITING 那樣一直等待。
這部分比較簡(jiǎn)單,它和線程等待狀態(tài)(WAITING)狀態(tài) 非常相似,區(qū)別就是方法的參數(shù)舒服傳入限制時(shí)間,在?Timed Waiting狀態(tài)時(shí)會(huì)等待超時(shí),之后由系統(tǒng)喚醒,或者也可以提前被通知喚醒如?notify
相關(guān)方法主要有:
1.Object.wait(long)2.Thread.join(long)3.LockSupport.parkNanos(long)4.LockSupport.parkUntil(long)5.Thread.sleep(long)
需要注意的是Thread.sleep(long),當(dāng)線程執(zhí)行sleep方法時(shí),不會(huì)釋放當(dāng)前的鎖(如果當(dāng)前線程進(jìn)入了同步鎖),也不會(huì)讓出CPU。sleep(long)可以用指定時(shí)間使它自動(dòng)喚醒過(guò)來(lái),如果時(shí)間不到只能調(diào)用interrupt方法強(qiáng)行打斷。