Java中實(shí)現(xiàn)多線程的正確姿勢(反駁網(wǎng)上的某些觀點(diǎn))

“java中實(shí)現(xiàn)多線程到底有幾種方式?為啥我去搜索答案說幾種的都有。。?!弊罱笥褑栁疫@樣一個(gè)問題,當(dāng)時(shí)我腦子里條件反射的答案是兩種。但是他這么問,我也有點(diǎn)心虛,于是我也試著打開搜索引擎搜索了一下答案,發(fā)現(xiàn)說幾種的都有:兩種、三種、四種、六種。那到底應(yīng)該是幾種呢?:

于是我打開Oracle的java官方文檔去尋找答案,官方的答案是兩種。

  • 實(shí)現(xiàn)Runnable接口
public class ImplRunnableStyle implements Runnable {
    @Override
    public void run() {
        System.out.println("使用Runnable方式實(shí)現(xiàn)多線程");
    }

    public static void main(String[] args) {
        new Thread(new ImplRunnableStyle()).start();
    }
}
  • 繼承Thread類
public class ExtendsThreadStyle extends Thread {
    @Override
    public void run() {
        System.out.println("使用繼承Thread方式實(shí)現(xiàn)多線程");
    }

    public static void main(String[] args) {
        new ExtendsThreadStyle().start();
    }
}

在真正的編程環(huán)境中,推薦使用的方式還是實(shí)現(xiàn)Runnable接口的方式,原因這里有3點(diǎn):

  1. 從架構(gòu)角度來看,使用Runnable的方式可以將具體的任務(wù)(run方法)和創(chuàng)建、運(yùn)行線程的機(jī)制(Thread類)解耦,代碼結(jié)構(gòu)更加優(yōu)雅;
  2. 如果使用Thread繼承的方式,那么每次想創(chuàng)建一個(gè)新的任務(wù)都必須創(chuàng)建一個(gè)新的獨(dú)立線程,這樣就會造成額外的資源損耗,如果使用Runnable和線程池就可以避免這樣無謂的損耗;
  3. 由于Java語言不支持多繼承,如果使用繼承Thread的方式,就無法再繼承其他的類,限制了可擴(kuò)展性。

聊到這里我們是不是可以結(jié)束這個(gè)問題了?NO,NO,用電視劇里面比較拉仇恨的話講,“就這?”
我們再來看一道程序執(zhí)行邏輯的思考題:

public class BothImplRunnableAndExtendsThread {

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("執(zhí)行實(shí)現(xiàn)Runnable接口方式的代碼塊");
            }
        }) {
            @Override
            public void run() {
                System.out.println("執(zhí)行繼承Thread方式的代碼塊");
            }
        }.start();
    }
}

假如我們這兩種方式一起使用,會執(zhí)行哪個(gè)代碼塊?
咋一看這個(gè)題是不是感覺有點(diǎn)懵逼,正常開發(fā)的時(shí)候誰會這么寫代碼。。。但是我想說,如果你能不運(yùn)行程序就知道這道題的答案,并且可以給出原因,那說明你對上面講的兩種實(shí)現(xiàn)線程的方式才有了真正的理解。
我先給出程序的執(zhí)行結(jié)果:控制臺會輸出“執(zhí)行繼承Thread方式的代碼塊”,原因呢?我們?nèi)タ匆幌耇hread類run方法的源碼就了解了:

/**
     * If this thread was constructed using a separate
     * <code>Runnable</code> run object, then that
     * <code>Runnable</code> object's <code>run</code> method is called;
     * otherwise, this method does nothing and returns.
     * <p>
     * Subclasses of <code>Thread</code> should override this method.
     *
     * @see     #start()
     * @see     #stop()
     * @see     #Thread(ThreadGroup, Runnable, String)
     */
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

代碼很簡單,如果target!=null就執(zhí)行target.run()方法。那target又是什么?

看了Thread類的源碼應(yīng)該很明白了吧,target就是我們傳過去的實(shí)現(xiàn)Runnable接口的對象,那剛才那道思考題的答案為什么是“執(zhí)行繼承Thread方式的代碼塊”,小伙伴們也應(yīng)該很清楚了。因?yàn)楫?dāng)使用繼承Thread方式的時(shí)候,我們重寫了run方法,那么Thread類中run方法的代碼邏輯肯定就執(zhí)行不了了。所以只會執(zhí)行我們重寫的run方法代碼中的邏輯。

下面我們再來聊一下網(wǎng)上某些博客中的“觀點(diǎn)”:

  • “線程池創(chuàng)建線程也算是一種新建線程的方法”
public class ThreadPool {

    public static void main(String[] args) {
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        cachedThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println("線程池的方式創(chuàng)建線程");
            }
        });
    }
}

上面是通過線程池創(chuàng)建線程的簡單代碼,國際慣例我們直接看線程池的源碼是怎樣創(chuàng)建線程的:

上面是ThreadPoolExecutor類的execute方法源碼,直接看創(chuàng)建線程的關(guān)鍵代碼addWorker(command, true):
應(yīng)該已經(jīng)很明顯了吧,創(chuàng)建新線程肯定是Worker類中成員變量thread和firstTask一起完成的,這里thread是通過線程池的線程工廠去創(chuàng)建的,我們繼續(xù)跟進(jìn)源碼:
眼熟不眼熟,是不是上面提到的通過實(shí)現(xiàn)Runnable方式創(chuàng)建線程,所以所謂的通過線程池方式創(chuàng)建線程只是線程池的相關(guān)類包裝了一下而已,我們應(yīng)該透過現(xiàn)象看本質(zhì)。

網(wǎng)上還有很多其他的說法例如:“通過Callable和FutureTask創(chuàng)建線程”、“定時(shí)器創(chuàng)建線程”等等其實(shí)都是一種包裝,用趙本山的話講“你以為穿上馬甲我就不認(rèn)識你了?”

最后我們總結(jié)下:
我們通過新建Thread類的方式創(chuàng)建一個(gè)獨(dú)立線程,但是類里面的run方法有兩種方式來實(shí)現(xiàn):第一種是重寫Thread類的run方法;第二種是實(shí)現(xiàn)Runnable接口的run方法,然后再把該Runnable接口的實(shí)例傳給Thread類。除此以外,所謂的線程池、定時(shí)器等工具類也可以創(chuàng)建線程,但是它們的本質(zhì)都逃不過Java官方文檔提到的兩種方式。

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

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