Java多線程詳解

多線程作為Java中很重要的一個(gè)知識(shí)點(diǎn),在此還是有必要總結(jié)一下的。

先看一下相關(guān)圖例

線程的生命周期

1. Java線程具有五中基本狀態(tài)


  • 新建狀態(tài)(New):當(dāng)線程對象對創(chuàng)建后,即進(jìn)入了新建狀態(tài),如:Thread t = new MyThread();

  • 就緒狀態(tài)(Runnable):當(dāng)調(diào)用線程對象的start()方法(t.start();),線程即進(jìn)入就緒狀態(tài)。處于就緒狀態(tài)的線程,只是說明此線程已經(jīng)做好了準(zhǔn)備,隨時(shí)等待CPU調(diào)度執(zhí)行,并不是說執(zhí)行了t.start()此線程立即就會(huì)執(zhí)行;

  • 運(yùn)行狀態(tài)(Running):當(dāng)CPU開始調(diào)度處于就緒狀態(tài)的線程時(shí),此時(shí)線程才得以真正執(zhí)行,即進(jìn)入到運(yùn)行狀態(tài)。注:就 緒狀態(tài)是進(jìn)入到運(yùn)行狀態(tài)的唯一入口,也就是說,線程要想進(jìn)入運(yùn)行狀態(tài)執(zhí)行,首先必須處于就緒狀態(tài)中;

  • 阻塞狀態(tài)(Blocked):處于運(yùn)行狀態(tài)中的線程由于某種原因,暫時(shí)放棄對CPU的使用權(quán),停止執(zhí)行,此時(shí)進(jìn)入阻塞狀態(tài),直到其進(jìn)入到就緒狀態(tài),才 有機(jī)會(huì)再次被CPU調(diào)用以進(jìn)入到運(yùn)行狀態(tài)。根據(jù)阻塞產(chǎn)生的原因不同,阻塞狀態(tài)又可以分為三種:
  1. 等待阻塞:運(yùn)行狀態(tài)中的線程執(zhí)行wait()方法,使本線程進(jìn)入到等待阻塞狀態(tài);
  2. 同步阻塞 -- 線程在獲取synchronized同步鎖失敗(因?yàn)殒i被其它線程所占用),它會(huì)進(jìn)入同步阻塞狀態(tài);
  3. 其他阻塞 -- 通過調(diào)用線程的sleep()或join()或發(fā)出了I/O請求時(shí),線程會(huì)進(jìn)入到阻塞狀態(tài)。當(dāng)sleep()狀態(tài)超時(shí)、join()等待線程終止或者超時(shí)、或者I/O處理完畢時(shí),線程重新轉(zhuǎn)入就緒狀態(tài)。

  • 死亡狀態(tài)(Dead):線程執(zhí)行完了或者因異常退出了run()方法,該線程結(jié)束生命周期。

2. Java多線程的創(chuàng)建及啟動(dòng)

Java中線程的創(chuàng)建常見有如三種基本形式

  1. 繼承Thread類,重寫該類的run()方法。
class MyThread extends Thread {
    private int i = 0;

    @Override
    public void run() {
         // todo: anything
    }
}
public class ThreadTest {
    public static void main(String[] args) {
           Thread myThread1 = new MyThread();     // 創(chuàng)建一個(gè)新的線程  myThread1  此線程進(jìn)入新建狀態(tài)
           Thread myThread2 = new MyThread();     // 創(chuàng)建一個(gè)新的線程 myThread2 此線程進(jìn)入新建狀態(tài)
           myThread1.start();                     // 調(diào)用start()方法使得線程進(jìn)入就緒狀態(tài)
           myThread2.start();                     // 調(diào)用start()方法使得線程進(jìn)入就緒狀態(tài)
    }
}

如上所示,繼承Thread類,通過重寫run()方法定義了一個(gè)新的線程類MyThread,其中run()方法的方法體代表了線程需要完成的任務(wù),稱之為線程執(zhí)行體。當(dāng)創(chuàng)建此線程類對象時(shí)一個(gè)新的線程得以創(chuàng)建,并進(jìn)入到線程新建狀態(tài)。通過調(diào)用線程對象引用的start()方法,使得該線程進(jìn)入到就緒狀態(tài),此時(shí)此線程并不一定會(huì)馬上得以執(zhí)行,這取決于CPU調(diào)度時(shí)機(jī)。

2.實(shí)現(xiàn)Runnable接口,并重寫該接口的run()方法,該run()方法同樣是線程執(zhí)行體,創(chuàng)建Runnable實(shí)現(xiàn)類的實(shí)例,并以此實(shí)例作為Thread類的target來創(chuàng)建Thread對象,該Thread對象才是真正的線程對象。

class MyRunnable implements Runnable {
    private int i = 0;

    @Override
    public void run() {
       // todo: anything
    }
}
public class ThreadTest {

    public static void main(String[] args) {
        Runnable myRunnable = new MyRunnable(); // 創(chuàng)建一個(gè)Runnable實(shí)現(xiàn)類的對象
        Thread thread1 = new Thread(myRunnable); // 將myRunnable作為Thread target創(chuàng)建新的線程
        thread1.start(); // 調(diào)用start()方法使得線程進(jìn)入就緒狀態(tài)
    }
}

Thread和Runnable之間到底是什么關(guān)系呢,當(dāng)執(zhí)行到Thread類中的run()方法時(shí),會(huì)首先判斷target是否存在,存在則執(zhí)行target中的run()方法,也就是實(shí)現(xiàn)了Runnable接口并重寫了run()方法的類中的run()方法。

3.使用Callable和Future接口創(chuàng)建線程。具體是創(chuàng)建Callable接口的實(shí)現(xiàn)類,并實(shí)現(xiàn)clall()方法。并使用FutureTask類來包裝Callable實(shí)現(xiàn)類的對象,且以此FutureTask對象作為Thread對象的target來創(chuàng)建線程。

public class ThreadTest {

    public static void main(String[] args) {
         // 創(chuàng)建MyCallable對象
        Callable<Integer> myCallable = new MyCallable();   
        //使用FutureTask來包裝MyCallable對象
        FutureTask<Integer> ft = new FutureTask<Integer>(myCallable);
        //FutureTask對象作為Thread對象的target創(chuàng)建新的線程 
        Thread thread = new Thread(ft);   
        thread.start();     
        try {
            //取得新創(chuàng)建的新線程中的call()方法返回的結(jié)果
            int sum = ft.get();           
            System.out.println("sum = " + sum);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

class MyCallable implements Callable<Integer> {
    // 與run()方法不同的是,call()方法具有返回值
    @Override
    public Integer call() {
        return 1;
    }

}

在實(shí)現(xiàn)Callable接口中,此時(shí)不再是run()方法了,而是call()方法,此call()方法作為線程執(zhí)行體,同時(shí)還具有返回值!在創(chuàng)建新的線程時(shí),是通過FutureTask來包裝MyCallable對象,同時(shí)作為了Thread對象的target。

三. Java多線程的就緒、運(yùn)行和死亡狀態(tài)

就緒狀態(tài)轉(zhuǎn)換為運(yùn)行狀態(tài):當(dāng)此線程得到處理器資源;

運(yùn)行狀態(tài)轉(zhuǎn)換為就緒狀態(tài):當(dāng)此線程主動(dòng)調(diào)用yield()方法或在運(yùn)行過程中失去處理器資源。

運(yùn)行狀態(tài)轉(zhuǎn)換為死亡狀態(tài):當(dāng)此線程線程執(zhí)行體執(zhí)行完畢或發(fā)生了異常。

此處需要特別注意的是:當(dāng)調(diào)用線程的yield()方法時(shí),線程從運(yùn)行狀態(tài)轉(zhuǎn)換為就緒狀態(tài),但接下來CPU調(diào)度就緒狀態(tài)中的哪個(gè)線程具有一定的隨機(jī)性,因此,可能會(huì)出現(xiàn)A線程調(diào)用了yield()方法后,接下來CPU仍然調(diào)度了A線程的情況。

由于實(shí)際的業(yè)務(wù)需要,常常會(huì)遇到需要在特定時(shí)機(jī)終止某一線程的運(yùn)行,使其進(jìn)入到死亡狀態(tài)。目前最通用的做法是設(shè)置一boolean型的變量,當(dāng)條件滿足時(shí),使線程執(zhí)行體快速執(zhí)行完畢。如:

public class ThreadTest {

    public static void main(String[] args) {

        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
        myRunnable.stopThread();
    }
}

class MyRunnable implements Runnable {

    private boolean stop;

    @Override
    public void run() {
        // todo: anything
    }

    public void stopThread() {
        this.stop = true;
    }

}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • Java多線程學(xué)習(xí) [-] 一擴(kuò)展javalangThread類 二實(shí)現(xiàn)javalangRunnable接口 三T...
    影馳閱讀 3,106評(píng)論 1 18
  • 本文主要講了java中多線程的使用方法、線程同步、線程數(shù)據(jù)傳遞、線程狀態(tài)及相應(yīng)的一些線程函數(shù)用法、概述等。 首先講...
    李欣陽閱讀 2,593評(píng)論 1 15
  • ??一個(gè)任務(wù)通常就是一個(gè)程序,每個(gè)運(yùn)行中的程序就是一個(gè)進(jìn)程。當(dāng)一個(gè)程序運(yùn)行時(shí),內(nèi)部可能包含了多個(gè)順序執(zhí)行流,每個(gè)順...
    OmaiMoon閱讀 1,801評(píng)論 0 12
  • 寫在前面的話: 這篇博客是我從這里“轉(zhuǎn)載”的,為什么轉(zhuǎn)載兩個(gè)字加“”呢?因?yàn)檫@絕不是簡單的復(fù)制粘貼,我花了五六個(gè)小...
    SmartSean閱讀 4,935評(píng)論 12 45
  • 基本概念 多線程: 指的是這個(gè)程序(一個(gè)進(jìn)程)運(yùn)行時(shí)產(chǎn)生了不止一個(gè)線程 并行: 多個(gè)cpu實(shí)例或者多臺(tái)機(jī)器同時(shí)執(zhí)行...
    孤獨(dú)的根號(hào)十二閱讀 4,391評(píng)論 1 11

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