Java線程的狀態(tài)

線程的狀態(tài)

  • 在Java源碼中給線程定義了6種狀態(tà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;
    }
  • NEW :線程剛被創(chuàng)建叽奥,還沒有調(diào)用start()之前就處于該狀態(tài)

  • RUNNABLE :在調(diào)用start()之后冲茸,線程就處于運行狀態(tài)通孽。Java線程中將就緒(ready)和運行中(running)兩種狀態(tài)籠統(tǒng)的稱為“運行仲义,我們知道齐蔽,操作系統(tǒng)對線程的調(diào)度多是搶占式的产弹。也就是說調(diào)用了start()之后不一定馬上可以執(zhí)行派歌,而是被放置在可運行線程池中,說明該線程是處于就緒狀態(tài),可以被運行了胶果,這個時候的線程在等待線程調(diào)度常挚,直到等到cpu時間分片到了才會真正的執(zhí)行。注釋中也說明了該問題:but it may be waiting for other resources from the operating system such as processor.

  • BLOCKED :處于BLOCKED表示該線程阻塞于鎖稽物。當線程在等待一個監(jiān)視器的鎖去進入一個synchronized block/method (同步[塊|方法])或者在調(diào)用了Object#wait()后被notify后重新進入synchronized block/method依舊需要等待該鎖奄毡,線程就會處于阻塞狀態(tài)

  • WAITING :處于WAITING表明該線程正在等待另一個線程執(zhí)行特定的操作。當調(diào)用了Object#wait()贝或、Thread#join()吼过、LockSupport#park()中的某一個方法(需注意是with no timeout的)之后。線程就會處于等待狀態(tài)咪奖。等待的是其它線程的通知或者終止狀態(tài)

  • TIMED_WAITING : 具有指定等待時間的等待線程的線程狀態(tài)盗忱。與WAITING不同的是該狀態(tài)在指定等待時間之后會自行返回。當調(diào)用了Thread.sleep羊赵、Object#wait(long)趟佃、Thread.join(long)LockSupport#parkNanos昧捷、LockSupport#parkUntil 方法之一后闲昭。線程就處于等待狀態(tài),在指定的時間沒有被喚醒或者等待線程沒有結(jié)束靡挥,會被系統(tǒng)自動喚醒序矩,正常退出

  • TERMINATED :終止狀態(tài),表明線程已經(jīng)執(zhí)行完了run方法跋破。該狀態(tài)只是代表著線程已經(jīng)執(zhí)行完了run方法簸淀,實際上操作系統(tǒng)里線程可能被注銷了,也可能復(fù)用給其它線程任務(wù)請求毒返。

狀態(tài)驗證

  • 首先來看線程沒有任何阻塞租幕、等待處理的情況
class ThreadTest {
    @Test
    fun test() {
        val thread = MyThread()
        println("thread state before start:${thread.state}")
        thread.start()
        Thread.sleep(200)   //睡200ms等待run執(zhí)行完成
       println("thread state after run :${thread.state}")
    }


    class MyThread :Thread(){
        override fun run() {
            super.run()
            println("thread state on run:$state")
        }
    }
}
  • 運行結(jié)果如下
線程狀態(tài)-NEW_RUNNABLE_TERMINATED.png
  • 由于搶占鎖而處于阻塞狀態(tài)
class ThreadTest {

    @Test
    fun test() {
        val lock = Object()
        val thread1 = MyThread("thread 1",lock)
        thread1.start()
        Thread.sleep(200) //等待200ms 讓thread1被調(diào)度執(zhí)行

        val thread2 = MyThread("thread 2",lock)
        thread2.start()
        Thread.sleep(200) //等待200ms 讓thread2被調(diào)度執(zhí)行 ,此時thread1已經(jīng)持有鎖了

        println("thread2 state :${thread2.state}")
    }


    class MyThread constructor(name: String,var lock:Object) : Thread(name) {
        override fun run() {
            super.run()
            println("$name try to hole the lock")
            synchronized(lock) {
                println("$name  hole the lock now")
                sleep(1000 * 1000)
            }
        }
    }
}
  • 運行結(jié)果如下
線程狀態(tài)_BLOCKED.png
  • 由于等待其它線程的特定操作而處于等待狀態(tài),直到被通知
class ThreadTest {

    @Test
    fun test() {
        val lock = Object()
        val thread1 = MyThread("thread 1",lock)
        thread1.start()
        Thread.sleep(200) //等待200ms 讓thread1被調(diào)度執(zhí)行,進入等待狀態(tài)

        println("thread1 state :${thread1.state}")
    }


    class MyThread constructor(name: String,var lock:Object) : Thread(name) {
        override fun run() {
            super.run()
            synchronized(lock){ //在調(diào)用lock之前需要使用synchronized語義綁定住被wait/notify的對象
                println("$name state:$state")
                lock.wait()
                println("$name state:$state")
            }
        }
    }
}
  • 運行結(jié)果如下
線程狀態(tài)_WAITING.png
class ThreadTest {

    @Test
    fun test() {
        val lock = Object()
        val thread1 = MyThread("thread 1",lock)
        thread1.start()
        Thread.sleep(200) //等待200ms 讓thread1被調(diào)度執(zhí)行,進入等待狀態(tài)

        println("thread1 state :${thread1.state}")

        synchronized(lock) {
            lock.notifyAll() //通知
        }

        Thread.sleep(200)
        println("thread1 state after notify:${thread1.state}")
    }


    class MyThread constructor(name: String,var lock:Object) : Thread(name) {
        override fun run() {
            super.run()
            synchronized(lock){ //在調(diào)用wait()之前需要使用synchronized語義綁定住被wait/notify的對象(獲取到鎖)
                println("$name state:$state")
                lock.wait()
                println("$name state:$state")
            }
        }
    }
}
  • 運行結(jié)果如下
線程狀態(tài)_等待喚醒.png
  • 具有指定等待時間的等待線程的線程狀態(tài)
class ThreadTest {

    @Test
    fun test() {
        val thread1 = MyThread("thread 1")
        Object().wait(1000)
        thread1.start()
        Thread.sleep(200) //等待200ms 讓thread1被調(diào)度執(zhí)行,進入等待狀態(tài)
        println("thread1 state :${thread1.state}")
        Thread.sleep(1000) //等待1000ms 讓線程run執(zhí)行完成
        println("thread1 state :${thread1.state}")
    }


    class MyThread constructor(name: String) : Thread(name) {
        override fun run() {
            super.run()
            println("$name state:$state")
            sleep(1000)
            println("$name state:$state")
        }
    }
}
  • 運行結(jié)果如下
線程狀態(tài)_TIMED_WAITING .png
  • Object#wait(long)進入等待狀態(tài)
class ThreadTest {

    @Test
    fun test() {
        val lock = Object()
        val thread1 = MyThread("thread 1",lock,false)
        thread1.start()
        Thread.sleep(200) //等待200ms 讓thread1被調(diào)度執(zhí)行,進入等待狀態(tài)
        println("thread1 state :${thread1.state}")

        Thread.sleep(3000) //等待3000ms 讓線程thread1 run能夠繼續(xù)執(zhí)行
        thread1.condition = true //讓條件滿足

        Thread.sleep(2000) ///等待2000ms 確保讓線程thread1 run執(zhí)行
        println("thread1 state :${thread1.state}")
    }


    class MyThread constructor(name: String,var lock:Object,var condition:Boolean) : Thread(name) {

        override fun run() {
            super.run()
            synchronized(lock){
                while (!condition){
                    println("$name state:$state")
                    lock.wait(1000)
                    println("$name continue run at ${System.currentTimeMillis()}")
                }

                println("do sth when meet the conditions")
                println("$name state:$state")
            }

        }
    }
}
  • 運行結(jié)果如下
TIMED_WAITING.png
  • 需要注意的是實際上每次thread 1由于while循環(huán)每次超時喚醒之后又會重新進入TIMED_WAITING拧簸,然后才 continue run 直到條件符合退出循環(huán)
  • TIMED_WAITING狀態(tài)之后是有一個最多等待時間的劲绪,在超時之后線程會自動被喚醒,無需手動notify狡恬,當然也可以提前通過notify喚醒線程珠叔。從上面的運行結(jié)果相信讀者可以很好的體會出WAITINGTIMED_WAITING的區(qū)別

狀態(tài)圖

  • 通過源碼注釋及上面的分析我們可以畫一個狀態(tài)圖
線程狀態(tài)圖.png

Object.wait、Thread.sleep弟劲、LockSupport.park祷安、等方法的異同

在前面的狀態(tài)圖中列舉了非常多方法,那么它們的的區(qū)別是什么呢兔乞?怎么使用它們才能保證線程的狀態(tài)是符合我們的預(yù)期的(程序的功能符合預(yù)期)

  • Object.wait

    1. 調(diào)用之前需要先獲取鎖汇鞭,也就是被synchronized原語綁定
    2. 調(diào)用之后線程會釋放占有的鎖 凉唐,從測試用例的調(diào)用過程就可以看出來。調(diào)用obj.wait的時候和其它多線程調(diào)用obj.notifyAll的時候都需要先獲取鎖霍骄,如果調(diào)用obj.wait的時候沒有釋放鎖台囱,那么別的線程就無法獲取該對象鎖來notify
    3. 不傳時間會一直阻塞,直到另一個線程調(diào)用notify()/notifyAll()將其喚醒读整,傳入時間簿训,如果其它線程一直沒有調(diào)用notify()/notifyAll(), 到了時間會自動喚醒米间。被喚醒之后如果立即獲得鎖强品,會繼續(xù)執(zhí)行,如果沒有獲得鎖屈糊,會進入同步隊列等待獲取鎖的榛,也就是不一定會執(zhí)行后面的邏輯
    4. 可響應(yīng)interrupt()中斷,并且拋出InterruptedException
    5. notify()/notifyAll()必須在wait()之后調(diào)用,否則會拋出IllegalMonitorStateException異常,并且無法指定喚醒哪個線程
  • Thread.join

    1. 從jdk源碼中可以看到該方法的實現(xiàn)的原理實際上利用了 synchronized(lock) {while (isAlive()) { lock.wait(0);}}實現(xiàn)的逻锐,也就是說調(diào)用該線程的join方法的時候?qū)嶋H上需要先獲取該線程對象的鎖夫晌,否則也是會被阻塞的
    2. 線程A調(diào)用線程B#join方法之后,線程A一直阻塞直到B為TERMINATED狀態(tài)即被喚醒昧诱,所以一般可用來實現(xiàn)線程的同步運行晓淀,即按順序執(zhí)行
    3. 可響應(yīng)interrupt()中斷,并且拋出InterruptedException
  • Thread.sleep

    1. 不會釋放當前線程占有的鎖資源
    2. 在指定時間之后自動喚醒
    3. 可響應(yīng)interrupt()中斷,并且拋出InterruptedException
  • LockSupport.park

    1. 調(diào)用之前無需獲取鎖
    2. 調(diào)用之后不會釋放當前線程占有的鎖資源
    3. 可響應(yīng)interrupt()中斷,不會拋出InterruptedException
    4. 可調(diào)用LockSupport.unpart(Thread)指定喚醒某一線程鳄哭,并且一定會執(zhí)行后面的邏輯
    5. LockSupport.unpart(Thread)可在LockSupport.park 之前調(diào)用要糊,unpart先調(diào)用之后纲熏,調(diào)用park不會阻塞妆丘,會直接運行
    6. 多次調(diào)用LockSupport.unpart(Thread),只能獲得一次許可局劲,無法疊加勺拣,只要調(diào)用LockSupport.park 就會被消費,隨后調(diào)用LockSupport.park 會被阻塞

關(guān)于鎖的釋放的補充

前面說到Object.wait是會釋放鎖的鱼填,那么是釋放Object的鎖還是釋放線程持有的所有鎖呢药有?關(guān)于Object.wait釋放鎖,我想你能搜到的99.9%的文章都是只說了這么一句——Object.wait是會釋放鎖苹丸。先來看一個例子

class ThreadTest {

    @Test
    fun test() {
        val lock = Object()
        val lock2 = Object()
        val thread = Thread1("thread 1",lock,lock2)
        thread.start()
        Thread.sleep(200)

        synchronized(lock2){  //先獲取lock2看下會不會被釋放
            synchronized(lock){
                lock.notifyAll()
            }
        }

        Thread.sleep(1000) //等待thread 1執(zhí)行完成

        println("thread1 state :${thread.state}")
    }


    class  Thread1  constructor(name: String,var lock:Object,var lock2:Object) : Thread(name){
        override fun run() {
            super.run()
            synchronized(lock){ //持有l(wèi)ock2鎖
                synchronized(lock2){ //持有l(wèi)ock2鎖
                    lock.wait()

                    println("$name 被喚醒")
                }
            }

        }
    }

}

  • 猜猜上面的運行結(jié)果是什么愤惰? 會被喚醒嗎?
main dump信息.png
thread 1 Dump信息.png
  • 關(guān)于Object.wait會釋放synchronized鎖住的Object鎖毋庸置疑赘理,如果不能釋放的話也就沒有notify一說了。實際上的運行結(jié)果是main thread 和 thread 1都阻塞了。仔細看一下線程的dump信息色建,可以看到thread 1處于WAITING狀態(tài)厦幅,并且它 - locked <0x000000076be35ff8>,而main thread被阻塞了蜘澜,處于BLOCKED,它- waiting to lock <0x000000076be35ff8>。這不正是前面分析過的响疚,由于進入synchronized(lock2)獲取不到鎖而處于BLOCKED狀態(tài)鄙信。所以說,Object.wait實際上是調(diào)用哪個哪個對象的wait方法忿晕,就釋放該對應(yīng)對象的鎖装诡。
  • 那么既然Thread.join方法實際上是依賴于Object.wait實現(xiàn)的,釋放哪個監(jiān)視器鎖也就清楚了
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末践盼,一起剝皮案震驚了整個濱河市慎王,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌宏侍,老刑警劉巖赖淤,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異谅河,居然都是意外死亡咱旱,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門绷耍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吐限,“玉大人,你說我怎么就攤上這事褂始≈畹洌” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵崎苗,是天一觀的道長狐粱。 經(jīng)常有香客問我,道長胆数,這世上最難降的妖魔是什么肌蜻? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮必尼,結(jié)果婚禮上蒋搜,老公的妹妹穿的比我還像新娘。我一直安慰自己判莉,他們只是感情好豆挽,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著券盅,像睡著了一般帮哈。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上渗饮,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天但汞,我揣著相機與錄音宿刮,去河邊找鬼。 笑死私蕾,一個胖子當著我的面吹牛僵缺,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播踩叭,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼磕潮,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了容贝?” 一聲冷哼從身側(cè)響起自脯,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎斤富,沒想到半個月后膏潮,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡满力,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年焕参,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片油额。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡叠纷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出潦嘶,到底是詐尸還是另有隱情涩嚣,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布掂僵,位于F島的核電站航厚,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏看峻。R本人自食惡果不足惜阶淘,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望互妓。 院中可真熱鬧,春花似錦坤塞、人聲如沸冯勉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽灼狰。三九已至,卻和暖如春浮禾,著一層夾襖步出監(jiān)牢的瞬間交胚,已是汗流浹背份汗。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蝴簇,地道東北人杯活。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像熬词,于是被迫代替她去往敵國和親旁钧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容