溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Java面試問(wèn)題——線程全面詳解總結(jié)

發(fā)布時(shí)間:2020-04-02 14:27:05 來(lái)源:網(wǎng)絡(luò) 閱讀:306 作者:愛(ài)碼仕i 欄目:編程語(yǔ)言

Java面試問(wèn)題——線程全面詳解總結(jié)

一、多線程是什么?為什么要用多線程?

介紹多線程之前要介紹線程,介紹線程則離不開(kāi)進(jìn)程。

首先進(jìn)程 :是一個(gè)正在執(zhí)行中的程序,每一個(gè)進(jìn)程執(zhí)行都有一個(gè)執(zhí)行順序,該順序是一個(gè)執(zhí)行路徑,或者叫一個(gè)控制單元;

線程:就是進(jìn)程中的一個(gè)獨(dú)立控制單元,線程在控制著進(jìn)程的執(zhí)行。一個(gè)進(jìn)程中至少有一個(gè)進(jìn)程。

多線程:一個(gè)進(jìn)程中不只有一個(gè)線程。

為什么要用多線程

  • 為了更好的利用cpu的資源,如果只有一個(gè)線程,則第二個(gè)任務(wù)必須等到第一個(gè)任務(wù)結(jié)束后才能進(jìn)行,如果使用多線程則在主線程執(zhí)行任務(wù)的同時(shí)可以執(zhí)行其他任務(wù),而不需要等待;
  • 進(jìn)程之間不能共享數(shù)據(jù),線程可以;
  • 系統(tǒng)創(chuàng)建進(jìn)程需要為該進(jìn)程重新分配系統(tǒng)資源,創(chuàng)建線程代價(jià)比較??;
  • Java語(yǔ)言內(nèi)置了多線程功能支持,簡(jiǎn)化了java多線程編程。

二、線程的生命周期:

新建 :從新建一個(gè)線程對(duì)象到程序start() 這個(gè)線程之間的狀態(tài),都是新建狀態(tài);

就緒 :線程對(duì)象調(diào)用start()方法后,就處于就緒狀態(tài),等到JVM里的線程調(diào)度器的調(diào)度;

運(yùn)行 :就緒狀態(tài)下的線程在獲取CPU資源后就可以執(zhí)行run(),此時(shí)的線程便處于運(yùn)行狀態(tài),運(yùn)行狀態(tài)的線程可變?yōu)榫途w、阻塞及死亡三種狀態(tài)。

等待/阻塞/睡眠 :在一個(gè)線程執(zhí)行了sleep(睡眠)、suspend(掛起)等方法后會(huì)失去所占有的資源,從而進(jìn)入阻塞狀態(tài),在睡眠結(jié)束后可重新進(jìn)入就緒狀態(tài)。

終止 :run()方法完成后或發(fā)生其他終止條件時(shí)就會(huì)切換到終止?fàn)顟B(tài)。

三、創(chuàng)建線程的方法(還有其他方法):

1、繼承Thread類(lèi):

步驟: 、定義類(lèi)繼承Thread;

  • 復(fù)寫(xiě)Thread類(lèi)中的run方法;

目的:將自定義代碼存儲(chǔ)在run方法,讓線程運(yùn)行

  • 調(diào)用線程的start方法:

該方法有兩步:?jiǎn)?dòng)線程,調(diào)用run方法。

2、實(shí)現(xiàn)Runnable接口:接口應(yīng)該由那些打算通過(guò)某一線程執(zhí)行其實(shí)例的類(lèi)來(lái)實(shí)現(xiàn)。類(lèi)必須定義一個(gè)稱為run 的無(wú)參方法。

實(shí)現(xiàn)步驟: 、定義類(lèi)實(shí)現(xiàn)Runnable接口

  • 覆蓋Runnable接口中的run方法
    將線程要運(yùn)行的代碼放在該run方法中。
  • 通過(guò)Thread類(lèi)建立線程對(duì)象。
  • 將Runnable接口的子類(lèi)對(duì)象作為實(shí)際參數(shù)傳遞給Thread類(lèi)的構(gòu)造函數(shù)。
    自定義的run方法所屬的對(duì)象是Runnable接口的子類(lèi)對(duì)象。所以要讓線程執(zhí)行指定對(duì)象的run方法就要先明確run方法所屬對(duì)象
  • 調(diào)用Thread類(lèi)的start方法開(kāi)啟線程并調(diào)用Runnable接口子類(lèi)的run方法。

3、通過(guò)Callable和Future創(chuàng)建線程:

實(shí)現(xiàn)步驟

  • 創(chuàng)建Callable接口的實(shí)現(xiàn)類(lèi),并實(shí)現(xiàn)call()方法,改方法將作為線程執(zhí)行體,且具有返回值。
  • 創(chuàng)建Callable實(shí)現(xiàn)類(lèi)的實(shí)例,使用FutrueTask類(lèi)進(jìn)行包裝Callable對(duì)象,F(xiàn)utureTask對(duì)象封裝了Callable對(duì)象的call()方法的返回值
  • 使用FutureTask對(duì)象作為T(mén)hread對(duì)象啟動(dòng)新線程。
  • 調(diào)用FutureTask對(duì)象的get()方法獲取子線程執(zhí)行結(jié)束后的返回值。

四、繼承Thread類(lèi)和實(shí)現(xiàn)Runnable接口、實(shí)現(xiàn)Callable接口的區(qū)別。

繼承Thread:線程代碼存放在Thread子類(lèi)run方法中。

  • 優(yōu)勢(shì):編寫(xiě)簡(jiǎn)單,可直接用this.getname()獲取當(dāng)前線程,不必使用Thread.currentThread()方法。
  • 劣勢(shì):已經(jīng)繼承了Thread類(lèi),無(wú)法再繼承其他類(lèi)。

實(shí)現(xiàn)Runnable:線程代碼存放在接口的子類(lèi)的run方法中。

  • 優(yōu)勢(shì):避免了單繼承的局限性、多個(gè)線程可以共享一個(gè)target對(duì)象,非常適合多線程處理同一份資源的情形。
  • 劣勢(shì):比較復(fù)雜、訪問(wèn)線程必須使用Thread.currentThread()方法、無(wú)返回值。

實(shí)現(xiàn)Callable

  • 優(yōu)勢(shì):有返回值、避免了單繼承的局限性、多個(gè)線程可以共享一個(gè)target對(duì)象,非常適合多線程處理同一份資源的情形。
  • 劣勢(shì):比較復(fù)雜、訪問(wèn)線程必須使用Thread.currentThread()方法建議使用實(shí)現(xiàn)接口的方式創(chuàng)建多線程。

五、線程狀態(tài)管理

1、線程睡眠---sleep:

線程睡眠的原因:線程執(zhí)行的太快,或需要強(qiáng)制執(zhí)行到下一個(gè)線程。

線程睡眠的方法(兩個(gè)):sleep(long millis)在指定的毫秒數(shù)內(nèi)讓正在執(zhí)行的線程休眠。

sleep(long millis,int nanos)在指定的毫秒數(shù)加指定的納秒數(shù)內(nèi)讓正在執(zhí)行的線程休眠。

線程睡眠的代碼演示:

public class SynTest {
    public static void main(String[] args) {
        new Thread(new CountDown(),"倒計(jì)時(shí)").start();
    }
}
class CountDown implements Runnable{
    int time = 10;
    public void run() {
        while (true) {
            if(time>=0){
                System.out.println(Thread.currentThread().getName() + ":" + time--);
                try {
                    Thread.sleep(1000);
                    //睡眠時(shí)間為1秒
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

每隔一秒則會(huì)打印一次,打印結(jié)果為:

倒計(jì)時(shí):10
倒計(jì)時(shí):9
倒計(jì)時(shí):8
倒計(jì)時(shí):7
倒計(jì)時(shí):6
倒計(jì)時(shí):5
倒計(jì)時(shí):4
倒計(jì)時(shí):3
倒計(jì)時(shí):2
倒計(jì)時(shí):1
倒計(jì)時(shí):0

擴(kuò)展
Java線程調(diào)度是Java多線程的核心,只有良好的調(diào)度,才能充分發(fā)揮系統(tǒng)的性能,提高程序的執(zhí)行效率。但是不管程序員怎么編寫(xiě)調(diào)度,只能最大限度的影響線程執(zhí)行的次序,而不能做到精準(zhǔn)控制。因?yàn)槭褂胹leep方法之后,線程是進(jìn)入阻塞狀態(tài)的,只有當(dāng)睡眠的時(shí)間結(jié)束,才會(huì)重新進(jìn)入到就緒狀態(tài),而就緒狀態(tài)進(jìn)入到運(yùn)行狀態(tài),是由系統(tǒng)控制的,我們不可能精準(zhǔn)的去干涉它,所以如果調(diào)用Thread.sleep(1000)使得線程睡眠1秒,可能結(jié)果會(huì)大于1秒。

總結(jié)

同步的前提

  1. 必須要有兩個(gè)或者兩個(gè)以上的線程。
  2. 必須是多個(gè)線程使用同一個(gè)鎖。
  3. 必須保證同步中只能有一個(gè)線程在運(yùn)行。
  4. 只能同步方法,不能同步變量和類(lèi)。
  5. 不必同步類(lèi)中所有方法,類(lèi)可以擁有同步和非同步的方法。
  6. 如果一個(gè)線程在對(duì)象上獲得一個(gè)鎖,就沒(méi)有任何其他線程可以進(jìn)入(該對(duì)象的)類(lèi)中的任何一個(gè)同步方法。
  7. 線程睡眠時(shí),它所持的任何鎖都不會(huì)釋放。
    • 好處:解決了多線程的安全問(wèn)題。
    • 弊端:多個(gè)線程需要判斷,消耗資源,降低效率。

如何找問(wèn)題?

  1. 明確哪些代碼是多線程運(yùn)行代碼。
  2. 明確共享數(shù)據(jù)。
  3. 明確多線程運(yùn)行代碼中哪些語(yǔ)句是操作共享數(shù)據(jù)的。

寫(xiě)在最后

Java面試問(wèn)題——線程全面詳解總結(jié)

  • 第一:看完點(diǎn)贊,感謝您對(duì)作者的認(rèn)可;
  • ...
  • 第二:隨手轉(zhuǎn)發(fā),分享知識(shí),讓更多人學(xué)習(xí)到;
  • ...
  • 第三:記得點(diǎn)關(guān)注,每天更新的!??!
  • ...
向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI