Java 并發(fā)編程 002 | 深入理解Thread類

概述

線程的狀態(tài)

Java 中線程中狀態(tài)可分為五種:New(新建狀態(tài))鲤看,Runnable(就緒狀態(tài)),Running(運(yùn)行狀態(tài))耍群,Blocked(阻塞狀態(tài))义桂,Dead(死亡狀態(tài))

  • New:新建狀態(tài)找筝,當(dāng)線程創(chuàng)建完成時(shí)為新建狀態(tài),即new Thread(...)慷吊,還沒(méi)有調(diào)用start方法時(shí)袖裕,線程處于新建狀態(tài)。

  • Runnable:就緒狀態(tài)溉瓶,當(dāng)調(diào)用線程的的start方法后急鳄,線程進(jìn)入就緒狀態(tài),等待CPU資源堰酿。處于就緒狀態(tài)的線程由Java運(yùn)行時(shí)系統(tǒng)的線程調(diào)度程序(thread scheduler)來(lái)調(diào)度疾宏。

  • Running:運(yùn)行狀態(tài),就緒狀態(tài)的線程獲取到CPU執(zhí)行權(quán)以后進(jìn)入運(yùn)行狀態(tài)触创,開(kāi)始執(zhí)行run方法坎藐。

  • Blocked:阻塞狀態(tài),線程沒(méi)有執(zhí)行完哼绑,由于某種原因(如岩馍,I/O操作等)讓出CPU執(zhí)行權(quán),自身進(jìn)入阻塞狀態(tài)抖韩。

  • Dead:死亡狀態(tài)蛀恩,線程執(zhí)行完成或者執(zhí)行過(guò)程中出現(xiàn)異常,線程就會(huì)進(jìn)入死亡狀態(tài)茂浮。

這五種狀態(tài)之間的轉(zhuǎn)換關(guān)系如下圖所示

image

Java中是如何實(shí)現(xiàn)這幾種狀態(tài)的轉(zhuǎn)換的

通過(guò) wait/notify/notifyAll 方法的使用

這三個(gè)方法都是在 Object 對(duì)象中定義的双谆,這里不做講解,具體可看 深入理解Object類的 wait 和 notify 這篇文章

通過(guò) sleep/yield/join 方法的使用

這三個(gè)方法都是在 Thread 對(duì)象中定義的励稳,本篇詳細(xì)講解

Thread類中常用的方法

start方法

start()用來(lái)啟動(dòng)一個(gè)線程佃乘,當(dāng)調(diào)用start方法后囱井,系統(tǒng)才會(huì)開(kāi)啟一個(gè)新的線程來(lái)執(zhí)行用戶定義的子任務(wù)驹尼,在這個(gè)過(guò)程中,會(huì)為相應(yīng)的線程分配需要的資源

run方法

run()方法是不需要用戶來(lái)調(diào)用的庞呕,當(dāng)通過(guò)start方法啟動(dòng)一個(gè)線程之后新翎,當(dāng)線程獲得了CPU執(zhí)行時(shí)間,便進(jìn)入run方法體去執(zhí)行具體的任務(wù)住练。注意地啰,繼承Thread類必須重寫run方法,在run方法中定義具體要執(zhí)行的任務(wù)

sleep方法

sleep相當(dāng)于讓線程睡眠讲逛,交出CPU亏吝,讓CPU去執(zhí)行其他的任務(wù)。

但是有一點(diǎn)要非常注意盏混,sleep方法不會(huì)釋放鎖蔚鸥,也就是說(shuō)如果當(dāng)前線程持有對(duì)某個(gè)對(duì)象的鎖惜论,則即使調(diào)用sleep方法,其他線程也無(wú)法訪問(wèn)這個(gè)對(duì)象止喷。

注意馆类,如果調(diào)用了sleep方法,必須捕獲InterruptedException異车或者將該異常向上層拋出乾巧。當(dāng)線程睡眠時(shí)間滿后,不一定會(huì)立即得到執(zhí)行预愤,因?yàn)榇藭r(shí)可能CPU正在執(zhí)行其他的任務(wù)沟于。所以說(shuō)調(diào)用sleep方法相當(dāng)于讓線程進(jìn)入阻塞狀態(tài)

yield方法

調(diào)用yield方法會(huì)讓當(dāng)前線程交出CPU權(quán)限植康,讓CPU去執(zhí)行其他的線程社裆。它跟sleep方法類似,同樣不會(huì)釋放鎖向图。但是yield不能控制具體的交出CPU的時(shí)間泳秀,另外,yield方法只能讓擁有相同優(yōu)先級(jí)的線程有獲取CPU執(zhí)行時(shí)間的機(jī)會(huì)榄攀。

注意嗜傅,調(diào)用yield方法并不會(huì)讓線程進(jìn)入阻塞狀態(tài),而是讓線程重回就緒狀態(tài)檩赢,它只需要等待重新獲取CPU執(zhí)行時(shí)間吕嘀,這一點(diǎn)是和sleep方法不一樣的。

join方法

假如在main線程中贞瞒,調(diào)用thread.join方法偶房,則main方法會(huì)等待thread線程執(zhí)行完畢或者等待一定的時(shí)間。如果調(diào)用的是無(wú)參join方法军浆,則等待thread執(zhí)行完畢棕洋,如果調(diào)用的是指定了時(shí)間參數(shù)的join方法,則等待一定的時(shí)間乒融。

實(shí)際上調(diào)用join方法是調(diào)用了Object的wait方法掰盘,這個(gè)可以通過(guò)查看源碼得知

/**
 * Waits at most {@code millis} milliseconds for this thread to
 * die. A timeout of {@code 0} means to wait forever.
 *
 * <p> This implementation uses a loop of {@code this.wait} calls
 * conditioned on {@code this.isAlive}. As a thread terminates the
 * {@code this.notifyAll} method is invoked. It is recommended that
 * applications not use {@code wait}, {@code notify}, or
 * {@code notifyAll} on {@code Thread} instances.
 *
 * @param  millis
 *         the time to wait in milliseconds
 *
 * @throws  IllegalArgumentException
 *          if the value of {@code millis} is negative
 *
 * @throws  InterruptedException
 *          if any thread has interrupted the current thread. The
 *          <i>interrupted status</i> of the current thread is
 *          cleared when this exception is thrown.
 */
public final synchronized void join(long millis)
throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;
    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }
    if (millis == 0) {
        while (isAlive()) {
            wait(0);
        }
    } else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

wait方法會(huì)讓線程進(jìn)入阻塞狀態(tài),并且會(huì)釋放線程占有的鎖赞季,并交出CPU執(zhí)行權(quán)限愧捕。由于wait方法會(huì)讓線程釋放對(duì)象鎖,所以join方法同樣會(huì)讓線程釋放對(duì)一個(gè)對(duì)象持有的鎖申钩。

這里可能有一個(gè)疑問(wèn)次绘,wait 方法調(diào)用后為什么沒(méi)有看到調(diào)用 notify 呢?

通過(guò)上面源碼的第六行注釋(As a thread terminates the {@code this.notifyAll} method is invoked)可以知道,實(shí)際上是在此線程上調(diào)用了需要加入的線程對(duì)象的wait()方法邮偎,加入的線程運(yùn)行完后罗洗,會(huì)自動(dòng)調(diào)用 notifyAll 方法,被 join 的線程自然能夠繼續(xù)執(zhí)行了钢猛。

interrupt方法

interrupt伙菜,顧名思義,即中斷的意思命迈。

單獨(dú)調(diào)用interrupt方法可以使得處于阻塞狀態(tài)的線程拋出一個(gè)異常贩绕,也就說(shuō),它可以用來(lái)中斷一個(gè)正處于阻塞狀態(tài)的線程壶愤;

直接調(diào)用interrupt方法不能中斷正在運(yùn)行中的線程淑倾;

如果配合isInterrupted()能夠中斷正在運(yùn)行的線程,因?yàn)檎{(diào)用interrupt方法相當(dāng)于將中斷標(biāo)志位置為true征椒,那么可以通過(guò)調(diào)用isInterrupted()判斷中斷標(biāo)志是否被置位來(lái)中斷線程的執(zhí)行娇哆。比如下面這段代碼:

public class Test {
     
    public static void main(String[] args) throws IOException  {
        Test test = new Test();
        MyThread thread = test.new MyThread();
        thread.start();
        try {
            Thread.currentThread().sleep(2000);
        } catch (InterruptedException e) {
             
        }
        thread.interrupt();
    } 
     
    class MyThread extends Thread{
        @Override
        public void run() {
            int i = 0;
            while(!isInterrupted() && i<Integer.MAX_VALUE){
                System.out.println(i+" while循環(huán)");
                i++;
            }
        }
    }
}

Thread類的一些屬性

Thread類實(shí)現(xiàn)了Runnable接口,在Thread類中勃救,有一些比較關(guān)鍵的屬性碍讨,比如name是表示Thread的名字,可以通過(guò)Thread類的構(gòu)造器中的參數(shù)來(lái)指定線程名字蒙秒,priority表示線程的優(yōu)先級(jí)(最大值為10勃黍,最小值為1,默認(rèn)值為5)晕讲,daemon表示線程是否是守護(hù)線程覆获,target表示要執(zhí)行的任務(wù)。

關(guān)于線程的優(yōu)先級(jí) priority

  • 對(duì)于優(yōu)先級(jí)設(shè)置的內(nèi)容可以通過(guò)Thread類的幾個(gè)常量來(lái)決定

    • 最高優(yōu)先級(jí):public final static int MAX_PRIORITY = 10;
    • 中等優(yōu)先級(jí):public final static int NORM_PRIORITY = 5;
    • 最低優(yōu)先級(jí):public final static int MIN_PRIORITY = 1;
  • 新創(chuàng)建的線程的優(yōu)先級(jí)就是普通優(yōu)先級(jí) Thread.NORM_PRIORITY

  • 繼承性:Java 默認(rèn)的線程優(yōu)先級(jí)是父線程的優(yōu)先級(jí)瓢省,也就是如果此時(shí)在線程A中啟動(dòng)一個(gè)線程B弄息,那么B和A的優(yōu)先級(jí)將是一樣的

  • 高優(yōu)先級(jí)的線程比低優(yōu)先級(jí)的線程有更高的幾率得到執(zhí)行,實(shí)際上這和操作系統(tǒng)及虛擬機(jī)版本相關(guān)勤婚,有可能即使設(shè)置了線程的優(yōu)先級(jí)也不會(huì)產(chǎn)生任何作用

關(guān)于守護(hù)線程 daemon

  • Java中有兩種線程:用戶線程和守護(hù)線程
  • 用戶線程和守護(hù)線程的區(qū)別:通過(guò)調(diào)用isDaemon()方法辨別摹量,返回false的為"用戶線程",否則為"守護(hù)線程"
  • 典型的守護(hù)線程是垃圾回收線程蛔六。只要當(dāng)前JVM進(jìn)程中存在任何一個(gè)還未結(jié)束的非守護(hù)線程荆永,守護(hù)線程就會(huì)一直工作,只有當(dāng)最后一個(gè)非守護(hù)線程結(jié)束時(shí)国章,守護(hù)線程才會(huì)隨著JVM一起停止工作
  • 主線程main為用戶線程

總結(jié)

線程各個(gè)狀態(tài)之間的轉(zhuǎn)化情況

image

sleep和yield有什么異同?

  • 二者都不會(huì)釋放鎖
  • sleep方法相當(dāng)于讓線程進(jìn)入阻塞狀態(tài)豆村,yield方法并不會(huì)讓線程進(jìn)入阻塞狀態(tài)液兽,而是讓線程重回就緒狀
  • yield不能控制具體的交出CPU的時(shí)間,另外,yield方法只能讓擁有相同優(yōu)先級(jí)的線程有獲取CPU執(zhí)行時(shí)間的機(jī)會(huì)

sleep和wait有什么異同四啰?

  • Thread.sleep()與Object.wait()二者都可以暫停當(dāng)前線程宁玫,釋放CPU控制權(quán)。
  • Object.wait()在釋放CPU同時(shí)柑晒,釋放了對(duì)象鎖的控制欧瘪;Thread.sleep()沒(méi)有對(duì)鎖釋放

參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末佛掖,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子涌庭,更是在濱河造成了極大的恐慌芥被,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件坐榆,死亡現(xiàn)場(chǎng)離奇詭異拴魄,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)席镀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門匹中,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人豪诲,你說(shuō)我怎么就攤上這事职员。” “怎么了跛溉?”我有些...
    開(kāi)封第一講書人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵焊切,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我芳室,道長(zhǎng)专肪,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任堪侯,我火速辦了婚禮嚎尤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘伍宦。我一直安慰自己芽死,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布次洼。 她就那樣靜靜地躺著关贵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪卖毁。 梳的紋絲不亂的頭發(fā)上揖曾,一...
    開(kāi)封第一講書人閱讀 49,816評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼炭剪。 笑死练链,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的奴拦。 我是一名探鬼主播媒鼓,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼错妖!你這毒婦竟也來(lái)了绿鸣?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤站玄,失蹤者是張志新(化名)和其女友劉穎枚驻,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體株旷,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡再登,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了晾剖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片锉矢。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖齿尽,靈堂內(nèi)的尸體忽然破棺而出沽损,到底是詐尸還是另有隱情,我是刑警寧澤循头,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布绵估,位于F島的核電站,受9級(jí)特大地震影響卡骂,放射性物質(zhì)發(fā)生泄漏国裳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一全跨、第九天 我趴在偏房一處隱蔽的房頂上張望缝左。 院中可真熱鬧,春花似錦浓若、人聲如沸渺杉。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)是越。三九已至,卻和暖如春诵原,著一層夾襖步出監(jiān)牢的瞬間英妓,已是汗流浹背挽放。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工绍赛, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蔓纠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓吗蚌,卻偏偏與公主長(zhǎng)得像腿倚,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蚯妇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348

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