Java線程基礎(chǔ)知識(shí)

線程的狀態(tài)

  • 新建狀態(tài):用new語(yǔ)句創(chuàng)建的線程對(duì)象處于新建狀態(tài),此時(shí)它和其它的java對(duì)象一樣羞反,僅僅在堆中被分配了內(nèi)存
  • 就緒狀態(tài):當(dāng)一個(gè)線程創(chuàng)建了以后,其他的線程調(diào)用了它的start()方法亏拉,該線程就進(jìn)入了就緒狀態(tài)星瘾。處于這個(gè)狀態(tài)的線程位于可運(yùn)行池中,等待獲得CPU的使用權(quán)
  • 運(yùn)行狀態(tài):處于這個(gè)狀態(tài)的線程占用CPU,執(zhí)行程序的代碼
  • 阻塞狀態(tài):當(dāng)線程處于阻塞狀態(tài)時(shí)钝的,java虛擬機(jī)不會(huì)給線程分配CPU翁垂,直到線程重新進(jìn)入就緒狀態(tài),它才有機(jī)會(huì)轉(zhuǎn)到運(yùn)行狀態(tài)硝桩。 可以細(xì)分為三種情況:
    • 位于對(duì)象等待池中的阻塞狀態(tài):當(dāng)線程運(yùn)行時(shí)沿猜,如果執(zhí)行了某個(gè)對(duì)象的wait()方法,java虛擬機(jī)就回把線程放到這個(gè)對(duì)象的等待池中
    • 位于對(duì)象鎖中的阻塞狀態(tài)碗脊,當(dāng)線程處于運(yùn)行狀態(tài)時(shí)啼肩,試圖獲得某個(gè)對(duì)象的同步鎖時(shí),如果該對(duì)象的同步鎖已經(jīng)被其他的線程占用,JVM就會(huì)把這個(gè)線程放到這個(gè)對(duì)象的瑣池中祈坠。
    • 其它的阻塞狀態(tài):當(dāng)前線程執(zhí)行了sleep()方法害碾,或者調(diào)用了其它線程的join()方法,或者發(fā)出了I/O請(qǐng)求時(shí)赦拘,就會(huì)進(jìn)入這個(gè)狀態(tài)中慌随。
shunxutu.png

線程的優(yōu)先級(jí)

  • 當(dāng)線程的優(yōu)先級(jí)沒(méi)有指定時(shí),所有線程都攜帶普通優(yōu)先級(jí)躺同。
  • 優(yōu)先級(jí)可以用從1到10的范圍指定阁猜。10表示最高優(yōu)先級(jí),1表示最低優(yōu)先級(jí)蹋艺,5是普通優(yōu)先級(jí)蹦漠。
  • 優(yōu)先級(jí)最高的線程在執(zhí)行時(shí)被給予優(yōu)先。但是不能保證線程在啟動(dòng)時(shí)就進(jìn)入運(yùn)行狀態(tài)车海。
  • 與在線程池中等待運(yùn)行機(jī)會(huì)的線程相比笛园,當(dāng)前正在運(yùn)行的線程可能總是擁有更高的優(yōu)先級(jí)。
  • t.setPriority()用來(lái)設(shè)定線程的優(yōu)先級(jí)侍芝。
  • 在線程開(kāi)始方法被調(diào)用之前研铆,線程的優(yōu)先級(jí)應(yīng)該被設(shè)定。
  • 你可以使用常量州叠,如MIN_PRIORITY,MAX_PRIORITY棵红,NORM_PRIORITY來(lái)設(shè)定優(yōu)先級(jí)
  /**
     * The minimum priority that a thread can have.
     */
    public final static int MIN_PRIORITY = 1;

   /**
     * The default priority that is assigned to a thread.
     */
    public final static int NORM_PRIORITY = 5;

    /**
     * The maximum priority that a thread can have.
     */
    public final static int MAX_PRIORITY = 10;

線程的使用

Thread t1 = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("t1 begin");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t1 end");
    }
});

t1.start();

線程中特殊函數(shù)

join()

join方法是一個(gè)屬于對(duì)象的方法,主要作用是是的調(diào)用join方法的這個(gè)線程對(duì)象先執(zhí)行咧栗,調(diào)用方法所在的線程等執(zhí)行完了逆甜,在執(zhí)行。

Thread t1 = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("t1 begin");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("t1 end");
    }
});

t1.start();
t1.join();

Thread t2 = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("t2 begin");
        System.out.println("t2 end");
    }
});
t2.start();

輸出的結(jié)果:


//注釋t1.join()


t1 begin

t2 begin

t2 end

t1 end

//沒(méi)有注釋t1.join()


t1 begin

t1 end

t2 begin

t2 end

wait()

表示等待獲取某個(gè)鎖執(zhí)行了該方法的線程釋放對(duì)象的鎖致板,JVM會(huì)把該線程放到對(duì)象的等待池中交煞。該線程等待其它線程喚醒 notify() 執(zhí)行該方法的線程喚醒在對(duì)象的等待池中等待的一個(gè)線程,JVM從對(duì)象的等待池中隨機(jī)選擇一個(gè)線程斟或,把它轉(zhuǎn)到對(duì)象的鎖池中素征。使線程由阻塞隊(duì)列進(jìn)入就緒狀態(tài)(只能在同步代碼塊中使用)上面尤其要注意一點(diǎn),一個(gè)線程被喚醒不代表立即獲取了對(duì)象的monitor萝挤,只有monitor御毅,只有等調(diào)用完notify()或者notifyAll()并退出synchronized塊,釋放對(duì)象鎖后怜珍,其余線程才可獲得鎖執(zhí)行

sleep()

是一個(gè)類(lèi)的方法端蛆,讓當(dāng)前線程停止執(zhí)行,讓出cpu給其他的線程酥泛,但是不會(huì)釋放對(duì)象鎖資源以及監(jiān)控的狀態(tài)今豆,當(dāng)指定的時(shí)間到了之后又會(huì)自動(dòng)恢復(fù)運(yùn)行狀態(tài)侈沪。有一個(gè)用法可以代替yield函數(shù)——sleep(0)

yield()

這方法與sleep()類(lèi)似,可以使用sleep(0)來(lái)達(dá)到相同的效果晚凿,只是不能由用戶指定暫停多長(zhǎng)時(shí)間,并且yield()方法只能讓同優(yōu)先級(jí)或者高優(yōu)先級(jí)的線程有執(zhí)行的機(jī)會(huì)瘦馍,注意這里并不是一定歼秽,有可能又會(huì)執(zhí)行當(dāng)前線程,執(zhí)行完后情组,這個(gè)線程的狀態(tài)從執(zhí)行狀態(tài)轉(zhuǎn)到了就緒狀態(tài)燥筷。

notify()

執(zhí)行該方法的線程喚醒在對(duì)象的等待池中等待的一個(gè)線程,JVM從對(duì)象的等待池中隨機(jī)選擇一個(gè)線程院崇,把它轉(zhuǎn)到對(duì)象的鎖池中肆氓。使線程由阻塞隊(duì)列進(jìn)入就緒狀態(tài)。注意:這里必須持有相同鎖的線程

interrupt()

中斷線程底瓣,被中斷線程會(huì)拋InterruptedException

線程的停止

當(dāng)線程啟動(dòng)時(shí)谢揪,我們?cè)趺慈ネV箚?dòng)的線程呢?一般來(lái)說(shuō)捐凭,有

run()和start()的區(qū)別

我們從源碼來(lái)學(xué)習(xí)拨扶,這兩個(gè)方法的不同,Thread類(lèi)的方法:


    /**
     * Package-scope method invoked by Dalvik VM to create "internal"
     * threads or attach threads created externally.
     *
     * Don't call Thread.currentThread(), since there may not be such
     * a thing (e.g. for Main).
     */
    Thread(ThreadGroup group, String name, int priority, boolean daemon) {
        synchronized (Thread.class) {
            id = ++Thread.count;
        }
        if (name == null) {
            this.name = "Thread-" + id;
        } else {
            this.name = name;
        }
        if (group == null) {
            throw new InternalError("group == null");
        }
        this.group = group;
        this.target = null;
        this.stackSize = 0;
        this.priority = priority;
        this.daemon = daemon;
        /* add ourselves to our ThreadGroup of choice */
        this.group.addThread(this);
    }
    /**
     * Initializes a new, existing Thread object with a runnable object,
     * the given name and belonging to the ThreadGroup passed as parameter.
     * This is the method that the several public constructors delegate their
     * work to.
     *
     * @param group ThreadGroup to which the new Thread will belong
     * @param runnable a java.lang.Runnable whose method <code>run</code> will
     *        be executed by the new Thread
     * @param threadName Name for the Thread being created
     * @param stackSize Platform dependent stack size
     * @throws IllegalThreadStateException if <code>group.destroy()</code> has
     *         already been done
     * @see java.lang.ThreadGroup
     * @see java.lang.Runnable
     */
//帶runnable參數(shù)的thread類(lèi)的構(gòu)造函數(shù)調(diào)用了這個(gè)方法
    private void create(ThreadGroup group, Runnable runnable, String threadName, long stackSize) {
        Thread currentThread = Thread.currentThread();
        if (group == null) {
            group = currentThread.getThreadGroup();
        }
        if (group.isDestroyed()) {
            throw new IllegalThreadStateException("Group already destroyed");
        }
        this.group = group;
        synchronized (Thread.class) {
            id = ++Thread.count;
        }
        if (threadName == null) {
            this.name = "Thread-" + id;
        } else {
            this.name = threadName;
        }
        //建立的runnable接口賦值給thread中的target
        this.target = runnable;
        this.stackSize = stackSize;
        this.priority = currentThread.getPriority();
        this.contextClassLoader = currentThread.contextClassLoader;
        // Transfer over InheritableThreadLocals.
        if (currentThread.inheritableValues != null) {
            inheritableValues = new ThreadLocal.Values(currentThread.inheritableValues);
        }
        // add ourselves to our ThreadGroup of choice
        this.group.addThread(this);
    }

run方法的源代碼:


 public void run() {
        if (target != null) {
            target.run();
        }
    }

在run方法中茁肠,直接調(diào)用的是我們傳入的target(Runnable對(duì)象)的run方法患民,并沒(méi)有開(kāi)啟新的線程

start方法的源代碼:


 public synchronized void start() {
        checkNotStarted();
        hasBeenStarted = true;
        nativeCreate(this, stackSize, daemon);
    }

start方法最后調(diào)用了nativeCreate的native方法,這個(gè)方法的主要作用是開(kāi)啟了一個(gè)新的線程垦梆。并且這個(gè)方法匹颤,會(huì)利用jni回調(diào)Thread的run方法。

總結(jié):

  1. 如果直接調(diào)用run方法托猩,并沒(méi)有開(kāi)啟新的線程印蓖,而是直接運(yùn)行run方法里面的內(nèi)容,
  2. 而start方法京腥,則會(huì)調(diào)用native方法 nativeCreate 開(kāi)啟線程

線程的停止

實(shí)際開(kāi)發(fā)中另伍,我們使用線程的場(chǎng)景一般是執(zhí)行耗時(shí)任務(wù),如果我們開(kāi)啟了多個(gè)新的線程來(lái)執(zhí)行新的任務(wù)绞旅,最后又不在對(duì)他進(jìn)行關(guān)閉摆尝,這樣有時(shí)候會(huì)浪費(fèi)資源和內(nèi)存的泄露。那我們?cè)趺磥?lái)管理我們的線程呢因悲?目前有兩種方法:

  • 我們自己手動(dòng)開(kāi)發(fā)堕汞,管理我們的線程,包括線程的啟動(dòng)晃琳,線程的回收讯检, 線程的停止等
  • 使用JDK中自帶的線程池技術(shù)

今天我們不講線程池琐鲁,后面的文章會(huì)講到。對(duì)于單個(gè)線程而言人灼,上面我們將了他的啟動(dòng)围段,現(xiàn)在我們來(lái)講他的關(guān)閉。

線程的關(guān)閉的二種方式:

1. 使用標(biāo)志位

我們定義一個(gè)標(biāo)志位投放,在線程的run方法中奈泪,不斷的循環(huán)檢測(cè)標(biāo)志位,從而確定是否退出

public class ShutdownThread extends Thread {  
    public volatile boolean exit = false;   
        public void run() {   
        while (!exit){  
            //do something  
        }  
    }   
}  
2. 使用interrupt方法

這里可以分為兩種情況:

  • 線程處于阻塞狀態(tài)灸芳,如使用了sleep涝桅,同步鎖的wait,socket的receiver烙样,accept等方法時(shí)冯遂,會(huì)使線程處于阻塞狀態(tài)。當(dāng)調(diào)用線程的interrupt()方法時(shí)谒获,系統(tǒng)會(huì)拋出一個(gè)InterruptedException異常蛤肌,代碼中通過(guò)捕獲異常,然后break跳出循環(huán)狀態(tài)批狱,使線程正常結(jié)束寻定。通常很多人認(rèn)為只要調(diào)用interrupt方法線程就會(huì)結(jié)束,實(shí)際上是錯(cuò)的精耐,一定要先捕獲InterruptedException異常之后通過(guò)break來(lái)跳出循環(huán)狼速,才能正常結(jié)束run方法。
public class ShutdownThread extends Thread {  
    public void run() {   
        while (true){  
            try{  
                    Thread.sleep(5*1000)卦停;阻塞5妙  
                }catch(InterruptedException e){  
                    e.printStackTrace();  
                    break;//捕獲到異常之后向胡,執(zhí)行break跳出循環(huán)。  
                }  
        }  
    }   
}   
  • 線程未進(jìn)入阻塞狀態(tài)惊完,使用isInterrupted()判斷線程的中斷標(biāo)志來(lái)退出循環(huán)僵芹,當(dāng)使用interrupt()方法時(shí),中斷標(biāo)志就會(huì)置true小槐,和使用自定義的標(biāo)志來(lái)控制循環(huán)是一樣的道理拇派。
public class ShutdownThread extends Thread {  
    public void run() {   
        while (!isInterrupted()){  
            //do something, but no tthrow InterruptedException  
        }  
    }   
}  

為什么要區(qū)分進(jìn)入阻塞狀態(tài)和和非阻塞狀態(tài)兩種情況了,是因?yàn)楫?dāng)阻塞狀態(tài)時(shí)凿跳,如果有interrupt()發(fā)生件豌,系統(tǒng)除了會(huì)拋出InterruptedException異常外,還會(huì)調(diào)用interrupted()函數(shù)控嗜,調(diào)用時(shí)能獲取到中斷狀態(tài)是true的狀態(tài)茧彤,調(diào)用完之后會(huì)復(fù)位中斷狀態(tài)為false,所以異常拋出之后通過(guò)isInterrupted()是獲取不到中斷狀態(tài)是true的狀態(tài)疆栏,從而不能退出循環(huán)曾掂,因此在線程未進(jìn)入阻塞的代碼段時(shí)是可以通過(guò)isInterrupted()來(lái)判斷中斷是否發(fā)生來(lái)控制循環(huán)惫谤,在進(jìn)入阻塞狀態(tài)后要通過(guò)捕獲異常來(lái)退出循環(huán)。

因此使用interrupt()來(lái)退出線程的最好的方式應(yīng)該是兩種情況都要考慮:

public class ThreadSafe extends Thread {  
    public void run() {   
        while (!isInterrupted()){ //非阻塞過(guò)程中通過(guò)判斷中斷標(biāo)志來(lái)退出  
            try{  
                Thread.sleep(5*1000)珠洗;//阻塞過(guò)程捕獲中斷異常來(lái)退出  
            }catch(InterruptedException e){  
                e.printStackTrace();  
                break;//捕獲到異常之后溜歪,執(zhí)行break跳出循環(huán)。  
            }  
        }  
    }   
}   
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末许蓖,一起剝皮案震驚了整個(gè)濱河市蝴猪,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蛔糯,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件窖式,死亡現(xiàn)場(chǎng)離奇詭異蚁飒,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)萝喘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)淮逻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人阁簸,你說(shuō)我怎么就攤上這事爬早。” “怎么了启妹?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵筛严,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我饶米,道長(zhǎng)桨啃,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任檬输,我火速辦了婚禮照瘾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘丧慈。我一直安慰自己析命,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布逃默。 她就那樣靜靜地躺著鹃愤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪完域。 梳的紋絲不亂的頭發(fā)上昼浦,一...
    開(kāi)封第一講書(shū)人閱讀 51,198評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音筒主,去河邊找鬼关噪。 笑死鸟蟹,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的使兔。 我是一名探鬼主播建钥,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼虐沥!你這毒婦竟也來(lái)了熊经?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤欲险,失蹤者是張志新(化名)和其女友劉穎镐依,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體天试,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡槐壳,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了喜每。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片务唐。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖带兜,靈堂內(nèi)的尸體忽然破棺而出枫笛,到底是詐尸還是另有隱情,我是刑警寧澤刚照,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布刑巧,位于F島的核電站,受9級(jí)特大地震影響无畔,放射性物質(zhì)發(fā)生泄漏海诲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一檩互、第九天 我趴在偏房一處隱蔽的房頂上張望特幔。 院中可真熱鬧,春花似錦闸昨、人聲如沸蚯斯。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)拍嵌。三九已至,卻和暖如春循诉,著一層夾襖步出監(jiān)牢的瞬間横辆,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工茄猫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留狈蚤,地道東北人困肩。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像脆侮,于是被迫代替她去往敵國(guó)和親锌畸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

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