作者:小牛呼嚕嚕 | https://xiaoniuhululu.com
計算機內功、JAVA底層、面試、職業成長相關資料等更多精彩文章在公眾號「小牛呼嚕嚕」
大家好,我是呼嚕嚕,最近一直在梳理Java併發,但內容雜且偏晦澀,今天我們一起來聊聊Java 執行緒的狀態及轉換 先來夯實一下基礎,萬丈高樓平地起,路還是得慢慢走。
Java執行緒的生命週期
我們先來看下Java執行緒的生命週期圖:
上圖也是本文的大綱,我們下面依次聊聊java各個執行緒狀態及其他們的轉換。
執行緒初始狀態
執行緒初始狀態(NEW): 當前執行緒處於執行緒被建立出來但沒有被呼叫start()
在Java執行緒的時間中,關於執行緒的一切的起點是從Thread 類的物件的建立開始,一般實現Runnable介面 或者 繼承Thread類的類,例項化一個物件出來,執行緒就進入了初始狀態
Thread thread = new Thread()
由於執行緒在我們作業系統中也是非常寶貴的資源,在實際開發中,我們常常用執行緒池來重複利用現有的執行緒來執行任務,避免多次建立和銷燬執行緒,從而降低建立和銷燬執行緒過程中的代價。Java 給我們提供了 Executor 介面來使用執行緒池,檢視其JDK1.8原始碼
,發現其內部封裝了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類原始碼中,我們還能發現執行緒狀態的列舉類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;
}
所謂執行緒的狀態,在java原始碼中都是透過threadStatus
的值來表示的
/* Java thread status for tools,
* initialized to indicate thread 'not yet started'
*/
private volatile int threadStatus = 0;
State
和 threadStatus
透過toThreadState
方法對映轉換
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;
}
}
到這裡我們就可以發現,Thread t = new Thread()
在Java中只是設定了執行緒的狀態,作業系統中並沒有的實際執行緒的建立
執行緒執行狀態
執行緒執行狀態(RUNNABLE),執行緒被呼叫了start()
等待執行的狀態
在Linux作業系統層面,包含Running
和 Ready
狀態。其中Ready狀態是等待 CPU 時間片。現今主流的JVM,比如hotspot虛擬機器都是把Java 執行緒,對映到作業系統OS底層的執行緒上,把排程委託給了作業系統。而作業系統比如Linux,它是多工作業系統,充分利用CPU的高效能,將CPU的時間分片,讓單個CPU實現"同時執行"多工的效果。
更多精彩文章在公眾號「小牛呼嚕嚕」
Linux的任務排程又採用搶佔式輪轉排程,我們不考慮特權程式的話,OS會選擇在CPU上佔用的時間最少程式,優先在cpu上分配資源,其對應的執行緒去執行任務,儘可能地維護任務排程公平。Running
和 Ready
狀態的執行緒在CPU中切換狀態非常短暫。大概只有 0.01 秒這一量級,區分開來意義不大,java將這2個狀態統一用RUNNABLE來表示
thread.start()原始碼解析
我們接下來看看為什麼說執行thread.start()
後,執行緒的才"真正的建立"
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()
方法的原始碼中,會去呼叫start0()
方法,而start0()
是private native void start0();
JVM呼叫Native方法的話,會進入到不受JVM控制的世界裡
在Thread類
例項化的同時,會首先呼叫registerNatives
方法,註冊本地Native方法,動態繫結JVM方法
private static native void registerNatives();
static {
registerNatives();
}
在Thread
類中透過registerNatives
將指定的本地方法繫結到指定函式,比如start0
本地方法繫結到JVM_StartThread
函式:
...
static JNINativeMethod methods[] = {
{"start0", "()V", (void *)&JVM_StartThread},
{"stop0", "(" OBJ ")V", (void *)&JVM_StopThread},
{"isAlive", "()Z", (void *)&JVM_IsThreadAlive},
...
原始碼見:http://hg.openjdk.java.net/jdk8u/jdk8u60/jdk/file/935758609767/src/share/native/java/lang/Thread.c
JVM_StartThread
是JVM層函式,拋去各種情況的處理,主要是透過 new JavaThread(&thread_entry, sz)
來建立JVM執行緒物件
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_StartThread");
JavaThread *native_thread = NULL;
//表示是否有異常,當丟擲異常時需要獲取Heap_lock。
bool throw_illegal_thread_state = false;
// 在釋出jvmti事件之前,必須釋放Threads_lock
// in Thread::start.
{
// 獲取 Threads_lock鎖
MutexLocker mu(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 running
jlong size =
java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
// 建立JVM執行緒(用JavaThread物件表示)
size_t sz = size > 0 ? (size_t) size : 0;
native_thread = new JavaThread(&thread_entry, sz);
...
}
}
...
Thread::start(native_thread);//啟動核心執行緒
JVM_END
原始碼見:https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/share/vm/prims/jvm.cpp
我們再來看看JavaThread
的實現,發現內部透過 os::create_thread(this, thr_type, stack_sz);
來呼叫不同作業系統的建立執行緒方法建立執行緒。
JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
Thread()
#if INCLUDE_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_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);//呼叫不同作業系統的建立執行緒方法建立執行緒
}
原始碼見:https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/share/vm/runtime/thread.cpp
我們都知道Java是跨平臺的,但是native各種方法底層c/c++程式碼對各平臺都需要有對應的相容,我們這邊以linux為例,其他平臺就大家自行去查閱了
bool os::create_thread(Thread* thread, ThreadType thr_type, size_t 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);
// stack size
if (os::Linux::supports_variable_stack_size()) {
// calculate stack size if it's not specified by caller
if (stack_size == 0) {
stack_size = os::Linux::default_stack_size(thr_type);
switch (thr_type) {
case os::java_thread:
// Java threads use ThreadStackSize which default value can be
// changed with the flag -Xss
assert (JavaThread::stack_size_at_create() > 0, "this should be set");
stack_size = JavaThread::stack_size_at_create();
break;
case os::compiler_thread:
if (CompilerThreadStackSize > 0) {
stack_size = (size_t)(CompilerThreadStackSize * K);
break;
} // else fall through:
// use VMThreadStackSize if CompilerThreadStackSize is not defined
case os::vm_thread:
case os::pgc_thread:
case os::cgc_thread:
case os::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 page
pthread_attr_setguardsize(&attr, os::Linux::default_guard_size(thr_type));
ThreadState state;
{
// Serialize thread creation if we are running with fixed stack LinuxThreads
bool lock = os::Linux::is_LinuxThreads() && !os::Linux::is_floating_stack();
if (lock) {
os::Linux::createThread_lock()->lock_without_safepoint_check();
}
pthread_t tid;
//透過pthread_create方法建立核心級執行緒 !
int ret = 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 far
thread->set_osthread(NULL);
delete osthread;
if (lock) os::Linux::createThread_lock()->unlock();
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();
MutexLockerEx ml(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 reached
if (state == ZOMBIE) {
thread->set_osthread(NULL);
delete osthread;
return false;
}
// The thread is returned suspended (in state INITIALIZED),
// and is started higher up in the call chain
assert(state == INITIALIZED, "race condition");
return true;
}
原始碼見:https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/os/linux/vm/os_linux.cpp
主要透過pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread)
,它是unix 建立執行緒的方法,linux也繼承了。呼叫後在linux系統中會建立一個核心級的執行緒。也就是說這個時候作業系統中執行緒才真正地誕生
更多精彩文章在公眾號「小牛呼嚕嚕」
但此時執行緒才誕生,那是怎麼啟動的?我們回到JVM_StartThread
原始碼中,Thread::start(native_thread)
很明顯這行程式碼就表示啟動native_thread = new JavaThread(&thread_entry, sz)
建立的執行緒,我們來繼續看看其原始碼
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()) {
// 設定執行緒狀態
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)
,執行該方法,作業系統會去啟動指定的執行緒
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);
}
當作業系統的執行緒啟動完之後,我們再回到pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread)
,會去java_start
這個執行緒入口函式進行OS核心級執行緒的初始化,並開始啟動JavaThread
// Thread start routine for all newly created threads
static void *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.
static int counter = 0;
int pid = 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 above
if (!_thread_safety_check(thread)) {
// notify parent thread
MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);
osthread->set_state(ZOMBIE);
sync->notify_all();
return NULL;
}
// thread_id is kernel thread id (similar to Solaris LWP id)
osthread->set_thread_id(os::Linux::gettid());
if (UseNUMA) {
int lgrp_id = os::numa_get_group_id();
if (lgrp_id != -1) {
thread->set_lgrp_id(lgrp_id);
}
}
// initialize signal mask for this thread
os::Linux::hotspot_sigmask(thread);
// initialize floating point control register
os::Linux::init_thread_fpu_state();
// handshaking with parent thread
{
MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);
// notify parent thread
osthread->set_state(INITIALIZED);
sync->notify_all();
// 等待,直到作業系統級執行緒全部啟動
while (osthread->get_state() == INITIALIZED) {
sync->wait(Mutex::_no_safepoint_check_flag);
}
}
// 開始執行JavaThread::run
thread->run();
return 0;
}
原始碼:https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/os/linux/vm/os_linux.cpp
thread->run()
其實就是JavaThread::run()
也表明方法開始回撥,從OS層方法回到JVM層方法
,我們再來看下其實現:
// 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物件中傳入的entry_point為Thread物件的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)
中引數entry_point
是外部傳入,那我們想想JavaThread
是什麼時候例項化的?
沒錯,就是我們一開始的JVM_StartThread
中native_thread = new JavaThread(&thread_entry, sz);
也就是說this->entry_point()(this, this)
實際上是回撥的thread_entry
方法
thread_entry
原始碼:
static void thread_entry(JavaThread* thread, TRAPS) {
HandleMark hm(THREAD);
Handle obj(THREAD, thread->threadObj());
JavaValue result(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
透過JavaCalls::call_virtual
方法,又從JVM層 回到了Java語言層 ,即MyThread thread = new MyThread(); thread.start();
一切又回到了起點,這就是Javathread.start()
內部完整的一個流程,HotSpot虛擬機器實現的Java執行緒其實是對Linux核心級執行緒的直接對映,將Java涉及到的所有執行緒排程、記憶體分配都交由作業系統進行管理。
執行緒終止狀態
執行緒終止狀態(TERMINATED),表示該執行緒已經執行完畢。
當一個執行緒執行完畢,或者主執行緒的main()方法完成時,我們就認為它終止了。終止的執行緒無法在被使用,如果呼叫start()
方法,會丟擲java.lang.IllegalThreadStateException
異常,這一點我們可以從start原始碼中很容易地得到
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
...
}
執行緒阻塞狀態
執行緒阻塞狀態(BLOCKED),需要等待鎖釋放或者說獲取鎖失敗時,執行緒阻塞
public class BlockedThread implements Runnable {
@Override
public void run() {
synchronized (BlockedThread.class){
while (true){
}
}
}
}
從Thread原始碼的註釋中,我們可以知道等待鎖釋放或者說獲取鎖失敗,主要有下面3中情況:
- 進入 synchronized 方法時
- 進入 synchronized 塊時
- 呼叫 wait 後, 重新進入 synchronized 方法/塊時
其中第三種情況,大家可以先思考一下,我們留在下文執行緒等待狀態
再詳細展開
執行緒等待狀態
執行緒等待狀態(WAITING),表示該執行緒需要等待其他執行緒做出一些特定動作(通知或中斷)。
wait/notify/notifyAll
我們緊接著上一小節,呼叫 wait 後, 重新進入synchronized 方法/塊時
,我們來看看期間發生了什麼?
當執行緒1呼叫物件A的wait
方法後,會釋放當前的鎖,然後讓出CPU時間片,執行緒會進入該物件的等待佇列中,執行緒狀態變為 等待狀態WAITING
。
當另一個執行緒2呼叫了物件A的notify()/notifyAll()
方法
notify()方法只會喚醒沉睡的執行緒,不會立即釋放之前佔有的物件A的鎖,必須執行完notify()方法所在的synchronized程式碼塊後才釋放。所以在程式設計中,儘量在使用了notify/notifyAll()後立即退出臨界區
執行緒1收到通知後退出等待佇列,並進入執行緒執行狀態RUNNABLE
,等待 CPU 時間片分配, 進而執行後續操作,接著執行緒1重新進入 synchronized 方法/塊時,競爭不到鎖,執行緒狀態變為執行緒阻塞狀態BLOCKED
。如果競爭到鎖,就直接接著執行。執行緒等待狀態 切換到執行緒阻塞狀態,無法直接切換,需要經過執行緒執行狀態。
我們再來看一個例子,鞏固鞏固:
public class WaitNotifyTest {
public static void main(String[] args) {
Object A = new Object();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("執行緒1等待獲取 物件A的鎖...");
synchronized (A) {
try {
System.out.println("執行緒1獲取了 物件A的鎖");
Thread.sleep(3000);
System.out.println("執行緒1開始執行wait()方法進行等待,進入到等待佇列......");
A.wait();
System.out.println("執行緒1等待結束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("執行緒2等待獲取 物件A的鎖...");
synchronized (A) {
System.out.println("執行緒2獲取了 物件A的鎖");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("執行緒2將要執行notify()方法進行喚醒執行緒1");
A.notify();
}
}
}).start();
}
}
結果:
執行緒1等待獲取 物件A的鎖...
執行緒1獲取了 物件A的鎖
執行緒2等待獲取 物件A的鎖...
執行緒1開始執行wait()方法進行等待,進入到等待佇列......
執行緒2獲取了 物件A的鎖
執行緒2將要執行notify()方法進行喚醒執行緒1
執行緒1等待結束
需要注意的是,wait/notify/notifyAll 只能在synchronized修飾的方法、塊中使用
, notify 是隻隨機喚醒一個執行緒,而 notifyAll 是喚醒所有等待佇列中的執行緒
join
Thread類中的join方法的主要作用能讓執行緒之間的並行執行變為序列執行,當前執行緒等該加入該執行緒後面,等待該執行緒終止
public static void main(String[] args) {
Thread thread = new Thread();
thread.start();
thread.join();
...
}
上面一個例子表示,程式在main主執行緒中呼叫thread執行緒的join方法,意味著main執行緒放棄CPU時間片(主執行緒會變成 WAITING 狀態),並返回thread執行緒,繼續執行直到執行緒thread執行完畢,換句話說在主執行緒執行過程中,插入thread執行緒,還得等thread執行緒執行完後,才輪到主執行緒繼續執行
如果檢視JDKthread.join()
底層實現,會發現其實內部封裝了wait(),notifyAll()
park/unpark
LockSupport.park() 掛起當前執行緒;LockSupport.unpark(暫停執行緒物件) 恢復某個執行緒
package com.zj.ideaprojects.demo.test3;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.LockSupport;
public class ThreadLockSupportTest {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
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);
}
}
結果:
start.....
park....
unpark....
resume.....
當程式呼叫LockSupport.park()
,會讓當前執行緒A的執行緒狀態會從 RUNNABLE 變成 WAITING,然後main主執行緒呼叫LockSupport.unpark(thread)
,讓指定的執行緒即執行緒A,從 WAITING 回到 RUNNABLE 。我們可以發現
park/unpark
和wait/notify/notifyAll
很像,但是他們有以下的區別:
- wait,notify 和 notifyAll 必須事先獲取物件鎖,而 unpark 不必
- park、unpark 可以先 unpark ,而 wait、notify 不能先 notify,必須先wait
- unpark 可以精準喚醒某一個確定的執行緒。而 notify 只能隨機喚醒一個等待執行緒,notifyAll 是喚醒所以等待執行緒,就不那麼精確
超時等待狀態
超時等待狀態(TIMED_WAITING),也叫限期等待,可以在指定的時間後自行返回而不是像 WAITING 那樣一直等待。
這部分比較簡單,它和執行緒等待狀態(WAITING)狀態 非常相似,區別就是方法的引數舒服傳入限制時間,在 Timed Waiting
狀態時會等待超時,之後由系統喚醒,或者也可以提前被通知喚醒如 notify
相關方法主要有:
1. Object.wait(long)
2. Thread.join(long)
3. LockSupport.parkNanos(long)
4. LockSupport.parkUntil(long)
5. Thread.sleep(long)
需要注意的是Thread.sleep(long)
,當執行緒執行sleep
方法時,不會釋放當前的鎖(如果當前執行緒進入了同步鎖),也不會讓出CPU。sleep(long)
可以用指定時間使它自動喚醒過來,如果時間不到只能呼叫interrupt
方法強行打斷。
參考資料:
https://hg.openjdk.java.net/jdk8u
《併發程式設計的藝術》
https://www.jianshu.com/p/216a41352fd8
本篇文章到這裡就結束啦,如果我的文章對你有所幫助,還請幫忙一鍵三連:點贊、關注、收藏,你的支援會激勵我輸出更高質量的文章,感謝!
原文映象:原來還能這樣看Java執行緒的狀態及轉換
計算機內功、原始碼解析、科技故事、專案實戰、面試八股等更多硬核文章,首發於公眾號「小牛呼嚕嚕」,我們下期再見!