溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Java線程生命周期的終止與復(fù)位怎么實(shí)現(xiàn)

發(fā)布時(shí)間:2022-09-23 10:36:35 來源:億速云 閱讀:128 作者:iii 欄目:開發(fā)技術(shù)

這篇文章主要介紹“Java線程生命周期的終止與復(fù)位怎么實(shí)現(xiàn)”,在日常操作中,相信很多人在Java線程生命周期的終止與復(fù)位怎么實(shí)現(xiàn)問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對(duì)大家解答”Java線程生命周期的終止與復(fù)位怎么實(shí)現(xiàn)”的疑惑有所幫助!接下來,請(qǐng)跟著小編一起來學(xué)習(xí)吧!

Thread生命周期

生命周期概述

Java的線程狀態(tài)描述放在Thread類里面的枚舉類State中.總共包含了6中狀態(tài)(從出生到死亡)。

public enum State {
    /**
     * 尚未啟動(dòng)的線程的線程狀態(tài) (沒有start)
     */
    NEW,

    /**
     * 可運(yùn)行線程的線程狀態(tài),是可以運(yùn)行的線程狀態(tài)(并不是在運(yùn)行)
     * 這個(gè)狀態(tài)在Java虛擬機(jī)中進(jìn)行,但它可能等待來自操作系統(tǒng)的其他資源,比如CPU。
     * 內(nèi)部包含了兩個(gè)狀態(tài) 【RUNNING】,【READY】這兩個(gè)狀態(tài)是可以互相流轉(zhuǎn)的
     * 調(diào)用了start后線程就處于 READY 狀態(tài) ,等待操作系統(tǒng)分配CPU時(shí)間片,分配后進(jìn)入 RUNNING 狀態(tài)。
     * 當(dāng)調(diào)用 yield() 方法后,只是謙讓的允許當(dāng)前線程讓出 CPU ,但是不一定讓,由操作系統(tǒng)決定,如果讓      * 了當(dāng)前線程就會(huì)進(jìn)入 READY 狀態(tài),等待系統(tǒng)分配CPU時(shí)間片再次進(jìn)入 RUNNING 狀態(tài)。
     */
    RUNNABLE,

    /**
     * 阻塞狀態(tài)。
     * 線程阻塞,等待監(jiān)視器鎖的狀態(tài),獲取監(jiān)視器鎖后會(huì)進(jìn)入 RUNNABLE  狀態(tài)
     * 當(dāng)發(fā)生線程鎖競(jìng)爭(zhēng)狀態(tài)下,沒有獲取到鎖的線程會(huì)被掛起進(jìn)入阻塞狀態(tài),比如synchronized鎖。
     */
    BLOCKED,

    /**
     * 等待線程的線程狀態(tài)
     * 線程調(diào)用以下方法會(huì)處于等待狀態(tài):Object.wait()不超時(shí)、Thread.join()不超時(shí)等方法
     * 一個(gè)處于等待狀態(tài)的線程正在等待另一個(gè)線程執(zhí)行特定動(dòng)作,例如:
     * 一個(gè)線程調(diào)用了Object.wait()方法在一個(gè)對(duì)象上正在等待另一個(gè)線程調(diào)用Object.nofify()或者
     * Object.nofifyAll()方法開啟那個(gè)對(duì)象
     * 一個(gè)調(diào)用了Thread.join()方法的線程正在等待指定線程終止
     */
    WAITING,

    /**
     * 具有指定等待時(shí)間的等待線程的線程狀態(tài),調(diào)用一下方法會(huì)處于這個(gè)狀態(tài): Object.wait() 超時(shí)、          * Thread.join()超時(shí) Thread.sleep(long) 等方法 
     */
    TIMED_WAITING,

    /**
     * 已終止線程的線程狀態(tài)
     * 線程執(zhí)行完畢或者發(fā)生異常終止執(zhí)行
     */
    TERMINATED;
}

線程生命周期流程圖

Java線程生命周期的終止與復(fù)位怎么實(shí)現(xiàn)

線程生命周期測(cè)試

public class ThreadStatusDemo {
    public static void main(String[] args) throws InterruptedException {
        // 測(cè)試 NEW RUNNABLE TERMINATED
        Thread terminated_thread = new Thread(() -> {
            long start = System.currentTimeMillis();
            // 運(yùn)行三秒 ,打印TERMINATED_THREAD線程runnable狀態(tài)
            while (System.currentTimeMillis()-start<3000){}
        }, "TERMINATED_THREAD");
        // NEW
        Thread.State state = terminated_thread.getState();
        System.out.println(terminated_thread.getName()+" :state = " + state);

        terminated_thread.start();
        TimeUnit.SECONDS.sleep(1);
        // RUNNABLE
        Thread.State state1 = terminated_thread.getState();
        System.out.println(terminated_thread.getName()+"state1 = " + state1);

        TimeUnit.SECONDS.sleep(5);
        Thread.State state2 = terminated_thread.getState();
        // TERMINATED
        System.out.println(terminated_thread.getName()+"state2 = " + state2);

        // RUNNABLE
        new Thread(() -> {
            while (true) {

            }
        }, "Runnle_Thread").start();
        // TIMED_WAITING
        new Thread(() -> {
            while (true) {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "Time_Waiting_Thread").start();
        // WAITING
        new Thread(() -> {
            while (true) {
                synchronized (ThreadStatusDemo.class) {
                    try {
                        ThreadStatusDemo.class.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }, "Waiting_Thread").start();

        // 這兩個(gè)看誰先搶占到cpu獲得鎖,另一個(gè)就blocked
        // timed_waiting
        new Thread(new BlockedDemo(), "Blocke01_Thread").start();
        // blocked
        new Thread(new BlockedDemo(), "Blocke02_Thread").start();
    }
    static class BlockedDemo extends Thread {
        @Override
        public void run() {
            synchronized (BlockedDemo.class) {
                while (true) {
                    try {
                        TimeUnit.SECONDS.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

Java線程生命周期的終止與復(fù)位怎么實(shí)現(xiàn)

Java線程生命周期的終止與復(fù)位怎么實(shí)現(xiàn)

啟動(dòng)線程

java中的啟動(dòng)

Java啟動(dòng)一個(gè)線程調(diào)用start方法,start方法內(nèi)部調(diào)用了 start0()native方法。

public synchronized void start() {
    . . .
    boolean started = false;
    try { 
        // 調(diào)用native方法
        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 */
        }
    }
}

這個(gè)測(cè)試是為了驗(yàn)證上圖的正確性,只貼了部分.

Hotspot中的啟動(dòng)

查看指引:

Java線程生命周期的終止與復(fù)位怎么實(shí)現(xiàn)

 在jvm.cpp找到JVM_StartThread方法。發(fā)現(xiàn)是先創(chuàng)建個(gè) JavaThread作為本地線程然后啟動(dòng)這個(gè)本地線程(借助os【thread.cpp】,因?yàn)閖vm是跨平臺(tái)的,這里是以linux-os為示例)

JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
  JVMWrapper("JVM_StartThread");
  JavaThread *native_thread = NULL;
  bool throw_illegal_thread_state = false;
  {
    MutexLocker mu(Threads_lock);

    if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
      throw_illegal_thread_state = true;
    } else {
      jlong size =
             java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
      size_t sz = size > 0 ? (size_t) size : 0;
      // 先創(chuàng)建一個(gè)JavaThread
      native_thread = new JavaThread(&thread_entry, sz);
      if (native_thread->osthread() != NULL) {
        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) {
    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");
  }
  // 然后啟動(dòng)這個(gè)本地線程 thread.cpp
  Thread::start(native_thread);
JVM_END

JavaThread 創(chuàng)建線程:

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);
  os::ThreadType thr_type = os::java_thread;
  thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread :
                                                     os::java_thread
// 調(diào)用os(操作系統(tǒng))創(chuàng)建個(gè)線程
  os::create_thread(this, thr_type, stack_sz);
  _safepoint_visible = false;
   . . .
}

thread.cpp 啟動(dòng)線程:

// tips: 啟動(dòng)線程的方法
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.
      // tips:啟動(dòng)之后設(shè)置線程的狀態(tài)為 可運(yùn)行狀態(tài) RUNNABLE
      java_lang_Thread::set_thread_status(((JavaThread*)thread)->threadObj(),
                                          java_lang_Thread::RUNNABLE);
    }
    // 借助操作系統(tǒng)啟動(dòng)線程
    os::start_thread(thread);
  }
}

線程中斷與復(fù)位

不要使用stop方法

線程的終止不要簡單的調(diào)用 stop方法,這個(gè)方法和其他的線程控制方法(suspend,resume)一樣都是過期了不建議使用的,這些方法都是不安全的。 例如stop()方法在結(jié)束一個(gè)線程的時(shí)候并不保證線程資源的正常釋放,因此可能導(dǎo)致出現(xiàn)一些不確定的狀態(tài)。 按照人類邏輯來理解:T1線程調(diào)用方法修改T2線程的狀態(tài),但是T2現(xiàn)在在做什么T1是不清楚的,所以強(qiáng)制他關(guān)閉就是不安全的,就好比在Linux中使用 kill -9 殺掉一個(gè)進(jìn)程。

使用interrupt方法

interrupt()方法只是修改了被中斷線程的中斷標(biāo)志 ,并沒有做什么過分的事兒。就像平時(shí)寫代碼的時(shí)候修改某對(duì)象的標(biāo)志,對(duì)象自己通過標(biāo)志類決定執(zhí)行什么邏輯。這里也是一樣,interrupt()方法修改中斷標(biāo)志,被中斷的線程,自己決定做什么事兒(中斷或者不中斷都是被中斷線程自己決定的,外部只是通知他,不是強(qiáng)迫他)。追一下源碼。

1.Java調(diào)用interrupt方法 

Java線程生命周期的終止與復(fù)位怎么實(shí)現(xiàn)

2.通過指引找到 jvm.cpp#JVM_Interrupt方法

Java線程生命周期的終止與復(fù)位怎么實(shí)現(xiàn)

thread.cpp interrupt 借用操作系統(tǒng)。直接通過系統(tǒng)調(diào)用 interrupt
void Thread::interrupt(Thread* thread) {
  trace("interrupt", thread);
  debug_only(check_for_dangling_thread_pointer(thread);)
  // tips: 調(diào)用操作系統(tǒng)的interrupt方法
  os::interrupt(thread);
}

這里還是以os_linux.cpp為例最終調(diào)用osthread的set_interrupted修改狀態(tài) 

Java線程生命周期的終止與復(fù)位怎么實(shí)現(xiàn)

Java線程生命周期的終止與復(fù)位怎么實(shí)現(xiàn)

這里就印證了上方的 Thread.interrupt()只是修改了線程的一個(gè)標(biāo)志位 ,并沒有做什么過分的事兒。

線程的復(fù)位

interruptedisInterrupted

這兩個(gè)放在一起是因?yàn)樗麄兊讓佣际钦{(diào)用的同一個(gè)native方法isInterrupted()只是給了不同的入?yún)ⅰ?再就是,有過面試官問到他兩的區(qū)別,所以干脆放在一起。首先說結(jié)論 ,isInterrupted()會(huì)返回線程的中斷狀態(tài),interrupted()不僅會(huì)返回中斷狀態(tài),而且如果線程處于狀態(tài)狀態(tài)還會(huì)將線程終端狀態(tài)復(fù)位(清除中斷狀態(tài))。

Java線程生命周期的終止與復(fù)位怎么實(shí)現(xiàn)

Java線程生命周期的終止與復(fù)位怎么實(shí)現(xiàn)

Java線程生命周期的終止與復(fù)位怎么實(shí)現(xiàn)

Java線程生命周期的終止與復(fù)位怎么實(shí)現(xiàn)

os_linux.cpp的is_interrupted()方法印證了上面說的isInterrupted()會(huì)返回線程的中斷狀態(tài),interrupted()不僅會(huì)返回中斷狀態(tài),而且如果線程處于狀態(tài)狀態(tài)還會(huì)將線程終端狀態(tài)復(fù)位(清除中斷狀態(tài))。

其他的線程復(fù)位

Java中只要拋出了InnterruptException異常的方法都對(duì)線程進(jìn)行了復(fù)位。先理順下為什么要這么做:查看下基本上拋出InnterruptException異常的方法都是線程阻塞方法,比如sleep(),wait(),join()。這類方法執(zhí)行后線程會(huì)處于TIMED_WAITING或者WAITING狀態(tài),處于這類狀態(tài)的線程是不受控的(線程喪失了對(duì)自己的主導(dǎo),需要其他的線程喚醒,或者阻塞時(shí)間到達(dá)才能擁有自己的主導(dǎo)權(quán)),這個(gè)時(shí)候線程中斷,線程自己卻沒辦法處理。甚至可能永遠(yuǎn)等不到釋放而無法執(zhí)行中斷。所以,在線程是中斷狀態(tài)下,執(zhí)行方法讓線程阻塞,就要拋出一個(gè)異常告訴外界 ,我現(xiàn)在是阻塞狀態(tài),并且將中斷標(biāo)記復(fù)位,方便外界進(jìn)行處理(例如中斷線程的執(zhí)行或者繼續(xù)阻塞方法),相當(dāng)于給了外界一個(gè)改變線程狀態(tài)的入口。 以sleep()為例追蹤下源碼:

Java線程生命周期的終止與復(fù)位怎么實(shí)現(xiàn)

通過指引找到 jcm.cpp#JVM_Sleep

Java線程生命周期的終止與復(fù)位怎么實(shí)現(xiàn)

方法入口就直接判斷線程的中斷狀態(tài)了 ,is_interrupted()上面介紹過了,參數(shù)為true就是清除中斷標(biāo)志并且返回清除之前的中斷狀態(tài)。這里線程是中斷狀態(tài)的就直接拋出 InnterruptException sleep interrupted異常了。

到此,關(guān)于“Java線程生命周期的終止與復(fù)位怎么實(shí)現(xiàn)”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI