溫馨提示×

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

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

并發(fā)編程專(zhuān)題(三)-線(xiàn)程的狀態(tài)

發(fā)布時(shí)間:2020-05-03 10:04:44 來(lái)源:網(wǎng)絡(luò) 閱讀:300 作者:BRUCELIU9527 欄目:編程語(yǔ)言
1.線(xiàn)程狀態(tài)

Java中,線(xiàn)程的狀態(tài)使用一個(gè)枚舉類(lèi)型來(lái)描述的。這個(gè)枚舉一共有6個(gè)值: NEW(新建)RUNNABLE(運(yùn)行)、BLOCKED(鎖池)TIMED_WAITING(定時(shí)等待)、WAITING(等待)TERMINATED(終止、結(jié)束)。
但是大多數(shù)人的理解和上面的這六種還是有些差別,通常會(huì)加上阻塞狀態(tài),可運(yùn)行狀態(tài),掛起狀態(tài)。
在API中 java.lang.Thread.State 這個(gè)枚舉中給出了六種線(xiàn)程狀態(tài),
這是Thread類(lèi)描述線(xiàn)程狀態(tài)的枚舉類(lèi)的源代碼:

public enum State {
        /**
         * Thread state for a thread which has not yet started.
         */
        NEW,

        /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         */
        TERMINATED;
    }
2.大多數(shù)人對(duì)線(xiàn)程狀態(tài)以及狀態(tài)轉(zhuǎn)換的理解

線(xiàn)程從創(chuàng)建、運(yùn)行到結(jié)束總是處于下面五個(gè)狀態(tài)之一:新建狀態(tài)、就緒狀態(tài)、運(yùn)行狀態(tài)、阻塞狀態(tài)及死亡狀態(tài)。
線(xiàn)程狀態(tài)轉(zhuǎn)換圖:
并發(fā)編程專(zhuān)題(三)-線(xiàn)程的狀態(tài)

2.1 新建狀態(tài)

當(dāng)用new操作符創(chuàng)建一個(gè)線(xiàn)程時(shí), 例如new Thread(r),線(xiàn)程還沒(méi)有開(kāi)始運(yùn)行,此時(shí)線(xiàn)程處在新建狀態(tài)。 當(dāng)一個(gè)線(xiàn)程處于新生狀態(tài)時(shí),程序還沒(méi)有開(kāi)始運(yùn)行線(xiàn)程中的代碼。

2.2 就緒狀態(tài)

一個(gè)新創(chuàng)建的線(xiàn)程并不自動(dòng)開(kāi)始運(yùn)行,要執(zhí)行線(xiàn)程,必須調(diào)用線(xiàn)程的start()方法。當(dāng)線(xiàn)程對(duì)象調(diào)用start()方法即啟動(dòng)了線(xiàn)程,start()方法創(chuàng)建線(xiàn)程運(yùn)行的系統(tǒng)資源,并調(diào)度線(xiàn)程運(yùn)行run()方法。當(dāng)start()方法返回后,線(xiàn)程就處于就緒狀態(tài)。

處于就緒狀態(tài)的線(xiàn)程并不一定立即運(yùn)行run()方法,線(xiàn)程還必須同其他線(xiàn)程競(jìng)爭(zhēng)CPU時(shí)間,只有獲得CPU時(shí)間才可以運(yùn)行線(xiàn)程。因?yàn)樵趩蜟PU的計(jì)算機(jī)系統(tǒng)中,不可能同時(shí)運(yùn)行多個(gè)線(xiàn)程,一個(gè)時(shí)刻僅有一個(gè)線(xiàn)程處于運(yùn)行狀態(tài)。因此此時(shí)可能有多個(gè)線(xiàn)程處于就緒狀態(tài)。對(duì)多個(gè)處于就緒狀態(tài)的線(xiàn)程是由

Java運(yùn)行時(shí)系統(tǒng)的線(xiàn)程調(diào)度程序(thread scheduler)來(lái)調(diào)度的。

2.3 運(yùn)行狀態(tài)

當(dāng)線(xiàn)程獲得CPU時(shí)間后,它才進(jìn)入運(yùn)行狀態(tài),真正開(kāi)始執(zhí)行run()方法.

2.4 阻塞狀態(tài)

線(xiàn)程運(yùn)行過(guò)程中,可能由于各種原因進(jìn)入阻塞狀態(tài):

  1. 線(xiàn)程通過(guò)調(diào)用sleep方法進(jìn)入睡眠狀態(tài);
    2.線(xiàn)程調(diào)用一個(gè)在I/O上被阻塞的操作,即該操作在輸入輸出操作完成之前不會(huì)返回到它的調(diào)用者;
    3.線(xiàn)程試圖得到一個(gè)鎖,而該鎖正被其他線(xiàn)程持有;
    4.線(xiàn)程在等待某個(gè)觸發(fā)條件;
2.5 死亡狀態(tài)

有兩個(gè)原因會(huì)導(dǎo)致線(xiàn)程死亡:
1.run方法正常退出而自然死亡,

  1. 一個(gè)未捕獲的異常終止了run方法而使線(xiàn)程猝死。
    為了確定線(xiàn)程在當(dāng)前是否存活著(就是要么是可運(yùn)行的,要么是被阻塞了),需要使用isAlive方法。如果是可運(yùn)行或被阻塞,這個(gè)方法返回true; 如果線(xiàn)程仍舊是new狀態(tài)且不是可運(yùn)行的, 或者線(xiàn)程死亡了,則返回false.
    3.對(duì)線(xiàn)程狀態(tài)以及狀態(tài)轉(zhuǎn)換的另外理解

    據(jù)官方源碼,一個(gè)線(xiàn)程有六個(gè)狀態(tài),沒(méi)有阻塞狀態(tài),沒(méi)有可運(yùn)行,沒(méi)有掛起狀態(tài)。這里先列出各個(gè)線(xiàn)程狀態(tài)發(fā)生的條件,下面將會(huì)對(duì)每種狀態(tài)進(jìn)行詳細(xì)解析:
    并發(fā)編程專(zhuān)題(三)-線(xiàn)程的狀態(tài)

線(xiàn)程狀態(tài) 導(dǎo)致?tīng)顟B(tài)發(fā)生修改的條件
NEW(新建) 線(xiàn)程剛被創(chuàng)建,但是并未啟動(dòng)。還沒(méi)調(diào)用start()方法。
Runnable(可運(yùn)行) 線(xiàn)程可以在java虛擬機(jī)中運(yùn)行的狀態(tài),可能正在運(yùn)行自己代碼,也可能沒(méi)有,這取決于操作系統(tǒng)處理器。
Blocked(鎖阻塞) 當(dāng)一個(gè)線(xiàn)程試圖獲取一個(gè)對(duì)象鎖,而該對(duì)象鎖被其他的線(xiàn)程持有,則該線(xiàn)程進(jìn)入Blocked狀態(tài);當(dāng)該線(xiàn)程持有鎖時(shí),該線(xiàn)程將變成Runnable狀態(tài)。
Waiting(無(wú)限 等待) 一個(gè)線(xiàn)程在等待另一個(gè)線(xiàn)程執(zhí)行一個(gè)(喚醒)動(dòng)作時(shí),該線(xiàn)程進(jìn)入Waiting狀態(tài)。進(jìn)入這個(gè)狀態(tài)后是不能自動(dòng)喚醒的,必須等待另一個(gè)線(xiàn)程調(diào)用notify或者notifyAll方法才能夠喚醒。
Timed Waiting(計(jì)時(shí)等待) 同waiting狀態(tài),有幾個(gè)方法有超時(shí)參數(shù),調(diào)用他們將進(jìn)入TimedWaiting狀態(tài)。這一狀態(tài)將一直保持到超時(shí)期滿(mǎn)或者接收到喚醒通知。帶有超時(shí)參數(shù)的常用方法有Thread.sleep、Object.wait。
Teminated(被 終止) 因?yàn)閞un方法正常退出而死亡,或者因?yàn)闆](méi)有捕獲的異常終止了run方法而死亡。

我們不需要去研究這幾種狀態(tài)的實(shí)現(xiàn)原理,我們只需知道在做線(xiàn)程操作中存在這樣的狀態(tài)。那我們?cè)趺慈ダ斫膺@幾個(gè)狀態(tài)呢,新建與被終止還是很容易理解的,我們就研究一下線(xiàn)程從Runnable(可運(yùn)行)狀態(tài)與非運(yùn)行狀態(tài)之間 的轉(zhuǎn)換問(wèn)題。

3.1 TimedWaiting(計(jì)時(shí)等待)

當(dāng)線(xiàn)程調(diào)用sleep()方法或當(dāng)前線(xiàn)程中有其他線(xiàn)程調(diào)用了帶時(shí)間參數(shù)的join()方法的時(shí)候進(jìn)入了定時(shí)等待狀態(tài)(TIMED_WAITING)。

代碼示例:

/**
 * @author bruceliu
 * @create 2019-06-01 22:35
 * @description TimedWaiting(計(jì)時(shí)等待)
 */
public class Test1 {
    public static void main(String[] args) {
        Thread1 t1 = new Thread1();
        Thread2 t2 = new Thread2();
        t1.setThread2(t2);

        t1.start();
        t2.start();
    }
}

//Thread1負(fù)責(zé)打印兩個(gè)線(xiàn)程的狀態(tài)。
class Thread1 extends Thread {
    private Thread2 t2;

    public void setThread2(Thread2 t2) {
        this.t2 = t2;
    }

    @Override
    public void run() {
        System.out.println("進(jìn)入t1線(xiàn)程");
        for (int i = 0; i < 5; i++) {
            try {
                System.out.println("t1 的狀態(tài): " + getState());
                System.out.println("t2 的狀態(tài): " + t2.getState());
                System.out.println();

                //為了減少打印次數(shù),所以t1每打印一次睡1秒
                Thread.sleep(1000);
            } catch (InterruptedException e) {

            }
        }
    }
}

class Thread2 extends Thread {
    @Override
    public void run() {
        System.out.println("進(jìn)入t2線(xiàn)程,馬上進(jìn)入睡眠");
        try {
            //睡眠5秒鐘。
            sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t2睡眠結(jié)束");
    }
}

通過(guò)案例可以發(fā)現(xiàn),sleep方法的使用還是很簡(jiǎn)單的。我們需要記住下面幾點(diǎn):

  1. 進(jìn)入 TIMED_WAITING 狀態(tài)的一種常見(jiàn)情形是調(diào)用的 sleep 方法,單獨(dú)的線(xiàn)程也可以調(diào)用,不一定非要有協(xié)作關(guān)系。
  2. 為了讓其他線(xiàn)程有機(jī)會(huì)執(zhí)行,可以將Thread.sleep()的調(diào)用放線(xiàn)程run()之內(nèi)。這樣才能保證該線(xiàn)程執(zhí)行過(guò)程中會(huì)睡眠
  3. sleep與鎖無(wú)關(guān),線(xiàn)程睡眠到期自動(dòng)蘇醒,并返回到Runnable(可運(yùn)行)狀態(tài)。

    sleep()中指定的時(shí)間是線(xiàn)程不會(huì)運(yùn)行的最短時(shí)間。因此,sleep()方法不能保證該線(xiàn)程睡眠到期后就開(kāi)始立刻執(zhí)行。

Timed Waiting 線(xiàn)程狀態(tài)圖:
并發(fā)編程專(zhuān)題(三)-線(xiàn)程的狀態(tài)

3.2 BLOCKED(鎖阻塞)

Blocked狀態(tài)在API中的介紹為:一個(gè)正在阻塞等待一個(gè)監(jiān)視器鎖(鎖對(duì)象)的線(xiàn)程處于這一狀態(tài)。
如果我們已經(jīng)學(xué)完同步機(jī)制,那么這個(gè)狀態(tài)是非常好理解的了。比如,線(xiàn)程A與線(xiàn)程B代碼中使用同一鎖,如果線(xiàn)程A獲取到鎖,線(xiàn)程A進(jìn)入到Runnable狀態(tài),那么線(xiàn)程B就進(jìn)入到Blocked鎖阻塞狀態(tài)。
這是由Runnable狀態(tài)進(jìn)入Blocked狀態(tài)。除此Waiting以及Time Waiting狀態(tài)也會(huì)在某種情況下進(jìn)入阻塞狀態(tài),而這部分內(nèi)容作為擴(kuò)充知識(shí)點(diǎn)帶領(lǐng)大家了解一下。

Blocked 線(xiàn)程狀態(tài)圖:
并發(fā)編程專(zhuān)題(三)-線(xiàn)程的狀態(tài)

3.3 Waiting(無(wú)限等待)

Wating狀態(tài)在API中介紹為:一個(gè)正在無(wú)限期等待另一個(gè)線(xiàn)程執(zhí)行一個(gè)特別的(喚醒)動(dòng)作的線(xiàn)程處于這一狀態(tài)。那么我們之前遇到過(guò)這種狀態(tài)嗎?答案是并沒(méi)有,但并不妨礙我們進(jìn)行一個(gè)簡(jiǎn)單深入的了解。我們通過(guò)一段代碼來(lái)學(xué)習(xí)一下:

package com.bruceliu.demo7;

/**
 * @author bruceliu
 * @create 2019-05-30 16:59
 * @description
 */
public class WaitingTest {

    public static Object obj = new Object();

    public static void main(String[] args) {

        // 演示waiting
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    synchronized (obj) {
                        try {
                            System.out.println(Thread.currentThread().getName() + "=== 從waiting狀態(tài)醒來(lái),獲取到鎖對(duì)象,繼續(xù)執(zhí)行了");
                            obj.wait(); //無(wú)限等待
                            //obj.wait(5000); //計(jì)時(shí)等待, 5秒 時(shí)間到,自動(dòng)醒來(lái)
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "=== 從waiting狀 態(tài)醒來(lái),獲取到鎖對(duì)象,繼續(xù)執(zhí)行了");
                    }
                }
            }
        }, "等待線(xiàn)程").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) { //每隔3秒 喚醒一次
                    try {
                        System.out.println(Thread.currentThread().getName() + "‐‐‐‐‐ 等待3秒鐘");
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (obj) {
                        System.out.println(Thread.currentThread().getName() + "‐‐‐‐‐ 獲取到鎖對(duì) 象,調(diào)用notify方法,釋放鎖對(duì)象");
                        obj.notify();
                    }
                }
            }
        }, "喚醒線(xiàn)程").start();
    }
}

通過(guò)上述案例我們會(huì)發(fā)現(xiàn),一個(gè)調(diào)用了某個(gè)對(duì)象的 Object.wait 方法的線(xiàn)程會(huì)等待另一個(gè)線(xiàn)程調(diào)用此對(duì)象的Object.notify()方法 或 Object.notifyAll()方法。其實(shí)waiting狀態(tài)并不是一個(gè)線(xiàn)程的操作,它體現(xiàn)的是多個(gè)線(xiàn)程間的通信,可以理解為多個(gè)線(xiàn)程之間的協(xié)作關(guān)系,多個(gè)線(xiàn)程會(huì)爭(zhēng)取鎖,同時(shí)相互之間又存在協(xié)作關(guān)系。就好比在公司里你和你的同事們,你們可能存在晉升時(shí)的競(jìng)爭(zhēng),但更多時(shí)候你們更多是一起合作以完成某些任務(wù)。當(dāng)多個(gè)線(xiàn)程協(xié)作時(shí),比如A,B線(xiàn)程,如果A線(xiàn)程在Runnable(可運(yùn)行)狀態(tài)中調(diào)用了wait()方法那么A線(xiàn)程就進(jìn)入了Waiting(無(wú)限等待)狀態(tài),同時(shí)失去了同步鎖。假如這個(gè)時(shí)候B線(xiàn)程獲取到了同步鎖,在運(yùn)行狀態(tài)中調(diào)用了notify()方法,那么就會(huì)將無(wú)限等待的A線(xiàn)程喚醒。注意是喚醒,如果獲取到鎖對(duì)象,那么A線(xiàn)程喚醒后就進(jìn)入Runnable(可運(yùn)行)狀態(tài);如果沒(méi)有獲取鎖對(duì)象,那么就進(jìn)入到Blocked(鎖阻塞狀態(tài))。

Waiting 線(xiàn)程狀態(tài)圖:
并發(fā)編程專(zhuān)題(三)-線(xiàn)程的狀態(tài)

3.4 小結(jié)

到此為止我們已經(jīng)對(duì)線(xiàn)程狀態(tài)有了基本的認(rèn)識(shí),想要有更多的了解,詳情可以見(jiàn)下圖:

并發(fā)編程專(zhuān)題(三)-線(xiàn)程的狀態(tài)

我們?cè)诜咥PI的時(shí)候會(huì)發(fā)現(xiàn)Timed Waiting(計(jì)時(shí)等待) 與 Waiting(無(wú)限等待) 狀態(tài)聯(lián)系還是很緊密的,比如Waiting(無(wú)限等待) 狀態(tài)中wait方法是空參的,而timed waiting(計(jì)時(shí)等待) 中wait方法是帶參的。這種帶參的方法,其實(shí)是一種倒計(jì)時(shí)操作,相當(dāng)于我們生活中的小鬧鐘,我們?cè)O(shè)定好時(shí)間,到時(shí)通知,可是如果提前得到(喚醒)通知,那么設(shè)定好時(shí)間在通知也就顯得多此一舉了,那么這種設(shè)計(jì)方案其實(shí)是一舉兩得。如果沒(méi)有得到(喚醒)通知,那么線(xiàn)程就處于Timed Waiting狀態(tài),直到倒計(jì)時(shí)完畢自動(dòng)醒來(lái);如果在倒計(jì)時(shí)期間得到(喚醒)通知,那么線(xiàn)程從Timed Waiting狀態(tài)立刻喚醒。

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

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

AI