Java線程學(xué)習(xí)(一)

  • Java使用Thread類(lèi)代表線程,所有的線程對(duì)象都必須是Tread類(lèi)或其子類(lèi)的實(shí)例。每條線程的作用是完成一定的任務(wù),實(shí)際上就是執(zhí)行一段程序流。Java使用run方法來(lái)封裝這樣的一段程序流。
    通過(guò)繼承Thread類(lèi)來(lái)創(chuàng)建并啟動(dòng)多線程的步驟如下:
  1. 定義Thread類(lèi)的子類(lèi),并重寫(xiě)該類(lèi)的run方法,該run方法的方法體就是代表了線程需要完成的任務(wù)。因此我們經(jīng)常把run方法稱(chēng)為線程執(zhí)行體。
  2. 創(chuàng)建Thread子類(lèi)的實(shí)例,即使創(chuàng)建了線程對(duì)象
  3. 用線程對(duì)象的start方法來(lái)啟動(dòng)該線程
public class FirstThread extends Thread{
        private int i;
        public void run(){
            for(;i < 100; i ++){
                //當(dāng)線程類(lèi)繼承Thread類(lèi)時(shí),可以直接調(diào)用getName()方法來(lái)返回當(dāng)前線程的名字
                //如果想獲取當(dāng)前線程,直接使用this即可
                //Thread對(duì)象的getName()返回該線程的名字
                System.out.println(getName() + "" + i);
            }
        }
        public static void main(){
                for(int i = 0; I < 100; i++){
                        System.out.println(Thread.currentThread().getName() + "" + i);
                        if (i == 20){
                                new FirstTread().start();
                                new FirstTread().start();
                        }
                }
        }
}
Thread.JPG

注意一:Thread-0 和Thread -1兩條線程輸出的i變量不連續(xù),i變量是FirstThread的實(shí)例屬性,而不是局部變量,但是因?yàn)槌绦蛎看蝿?chuàng)建線程對(duì)象時(shí)都需要?jiǎng)?chuàng)建一個(gè)FirstThread對(duì)象,所以不共享該實(shí)例屬性。使用繼承Thread類(lèi)的方法來(lái)創(chuàng)建線程類(lèi),多條線程之間無(wú)法共享線程類(lèi)的實(shí)例變量。
注意二:?jiǎn)?dòng)線程應(yīng)該使用start方法,而不是run方法。永遠(yuǎn)不要調(diào)用線程對(duì)象的run方法。調(diào)用start方法來(lái)啟動(dòng)線程,系統(tǒng)會(huì)把該run方法當(dāng)成線程執(zhí)行體來(lái)處理。但如果直接調(diào)用線程對(duì)象的run方法,系統(tǒng)指揮把線程對(duì)象當(dāng)作一個(gè)普通的對(duì)象,而run方法也只是普通方法,而不是線程執(zhí)行體。

package com.imooc;

public class MyThread extends Thread{
    private int i;
    public void run(){
        for (; i < 100; i++){
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }
    
    public static void main(String[] args){
        for (int i = 0; i < 100; i++){
            System.out.println(Thread.currentThread().getName() + " " + i);
            if (i ==20){
                new MyThread().run();
                new MyThread().run();
            }
        }
    }
}

  • 實(shí)現(xiàn)Runnable接口創(chuàng)建線程
    創(chuàng)建Runnable對(duì)象作為線程對(duì)象的target。使用這種方法時(shí),兩條線程的i變量是連續(xù)的,因?yàn)閮蓷l線程使用的是同一個(gè)target,即同一個(gè)Runnable對(duì)象。
public class SecondThread implements Runnable {

    private int i;

    @Override
    public void run() {
        // TODO Auto-generated method stub
        for(; i < 100; i ++){
            System.out.println(Thread.currentThread().getName() + " " + i);  //注意三
        }
    }

    public static void main(String[] args){
        for(int i = 0; i < 100; i++){
            System.out.println(Thread.currentThread().getName() + " " + i);
            if (i == 20){
                SecondThread st = new SecondThread();
                new Thread(st, "新線程1").start();
                new Thread(st, "新線程2").start();
            }
        }
    }
}

注意三:
使用Runnable接口時(shí),必須使用Thread.currentThread.getName()方法來(lái)查看當(dāng)前線程的名字

  • 線程的生命周期
  1. 新建:new關(guān)鍵字創(chuàng)建了一個(gè)線程后,該線程就處于新建狀態(tài)。此時(shí)和其他Java對(duì)象一樣,僅僅由虛擬機(jī)為其分配了內(nèi)存,并初始化了成員變量的值。沒(méi)有表現(xiàn)出任何線程的動(dòng)態(tài)特征。
  2. 就緒:當(dāng)線程對(duì)象調(diào)用了start()方法后,該線程就處于就緒狀態(tài),Java虛擬機(jī)會(huì)為其創(chuàng)建方法調(diào)用棧,程序計(jì)數(shù)器,但是此時(shí)還沒(méi)有運(yùn)行,只是代表著該線程可以運(yùn)行了。至于何時(shí)運(yùn)行,取決于JVM里線程調(diào)度器的調(diào)度。
  3. 運(yùn)行:如果就緒的線程獲得了CPU,開(kāi)始執(zhí)行run方法的線程執(zhí)行體,此時(shí)線程處于運(yùn)行狀態(tài)
  4. 阻塞:
    發(fā)生以下情況時(shí),線程進(jìn)入阻塞:
  • 線程調(diào)用sleep方法主動(dòng)放棄處理器資源
  • 等待資源時(shí)
  • 程序調(diào)用了線程的suspend方法將該線程掛起(容易導(dǎo)致死鎖)
針對(duì)以上的情況,發(fā)生特定的情況將可以接觸上面的阻塞,讓該線程重新進(jìn)入就緒狀態(tài):
- 調(diào)用的sleep方法經(jīng)過(guò)了指定的時(shí)間
- 等待的資源已獲得
- 處于掛起狀態(tài)的線程被調(diào)用了resume()回復(fù)方法。
  1. 死亡:我們可以通過(guò)調(diào)用線程對(duì)象的isAlive()方法來(lái)檢測(cè)線程是否死亡,當(dāng)線程處于就緒,運(yùn)行,阻塞時(shí),該方法返回true;處于新建,死亡時(shí),該方法返回false。注意,不要試圖對(duì)一個(gè)已經(jīng)死亡的線程調(diào)用start()方法,否則會(huì)產(chǎn)生IllegalThreadStateException異常。
    線程會(huì)以以下三種方式之一結(jié)束,結(jié)束后就處于死亡狀態(tài);
  • run()方法執(zhí)行完成,線程正常結(jié)束
  • 線程拋出一個(gè)未捕獲的Exception或Error
  • 直接調(diào)用該線程的stop()來(lái)結(jié)束該線程-該方法容易導(dǎo)致死鎖
  • 控制線程
  1. join線程:當(dāng)在某個(gè)程序執(zhí)行流中調(diào)用A線程的join()方法時(shí),調(diào)用線程將被阻塞,直到被join()方法加入的A線程完成為止。
  2. 后臺(tái)線程:這種線程在后臺(tái)運(yùn)行,他的任務(wù)是為其他的線程提供服務(wù)。如果所有的前臺(tái)線程死亡,后臺(tái)線程會(huì)自動(dòng)死亡。調(diào)用Thread對(duì)象的setDaemon(true)方法可將指定線程設(shè)置成后臺(tái)線程,但是注意,setDeamon(true)必須在start()方法前調(diào)用,否則IllegalThreadStateException異常。
  3. 線程睡眠sleep()方法:通過(guò)調(diào)用Thread類(lèi)的靜sleep方法,可以讓當(dāng)前正在執(zhí)行的線程暫定指定的時(shí)間,并進(jìn)入阻塞狀態(tài)Thread.sleep(1000);,在之后,線程自動(dòng)進(jìn)入就緒狀態(tài)。
  4. 線程讓步y(tǒng)ield方法:這個(gè)方法也是Thread提供的一個(gè)靜態(tài)方法,它將正在執(zhí)行的線程自動(dòng)轉(zhuǎn)入就緒狀態(tài)(但不阻塞)。當(dāng)某個(gè)線程調(diào)用了yield方法暫停后,線程調(diào)度器完全可能再次將其調(diào)用出來(lái)執(zhí)行。但是,當(dāng)某個(gè)線程調(diào)用了yield()方法暫停后,只有優(yōu)先級(jí)大于、等于當(dāng)前線程的就緒狀態(tài)的線程才會(huì)獲得執(zhí)行的機(jī)會(huì)。
  5. 改變線程的優(yōu)先級(jí):`thread.setPriority(MAX_PRIORITY)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容