Java 線程池模型的思考和選擇(已更新)

多謝 風(fēng)水 同學(xué)提出的問題。我重新研究了下源代碼,糾正下我之前存在的錯(cuò)誤的理論。于是對(duì)之前的文章做了修正,之前對(duì)大家引起的誤導(dǎo)表示非常道歉。

這里對(duì)之前的文章重新進(jìn)行了下梳理和研究,希望大家斧正。

---- 正文 -----
這幾天總是討論線程創(chuàng)建方式的問題,說性能比那個(gè)最優(yōu),哪個(gè)更方便等等。對(duì)于熟悉java的人來說,最先接觸的應(yīng)該是ExecutorService,這種線程池管理的方式操作比較簡單,也廣受大家好評(píng)。但是在很多系統(tǒng)上還有其他的實(shí)現(xiàn)方式,把Thread對(duì)象new出來,放進(jìn)容器里面。這些Thread不停的取Queue(有生產(chǎn)者會(huì)往里面放待處理數(shù)據(jù))里的數(shù)據(jù)進(jìn)行處理,不停進(jìn)行循環(huán)??梢缘絨ueue沒數(shù)據(jù)的時(shí)候進(jìn)行阻塞,直到有數(shù)據(jù)重新開始。

但是這里要說明一點(diǎn)的是,這兩者的原理是一樣的,只不過ExecutorService會(huì)借助Runnable來實(shí)現(xiàn)初始化線程,直到初始化滿為止,后續(xù)的Runnable執(zhí)行使用。具體的源碼分析在后續(xù)的文章中一起分析。

不過對(duì)于自己的實(shí)現(xiàn),和ExecutorService的實(shí)現(xiàn),雖然一樣,但是還是想看兩者的速度如何。于是我做了一個(gè)小實(shí)驗(yàn),來簡單判斷這兩者的區(qū)別

ExecutorService的方式:

public static void ExecutorServiceTest() {
    ExecutorService es = Executors.newFixedThreadPool(100);
    for(int i = 0;i<100;i++) {    // 進(jìn)行初始化各個(gè)線程,所以現(xiàn)用Runnable進(jìn)行補(bǔ)充
        es.execute(new Runnable() {         
            int i = 9;
            @Override
            public void run() {
                int b =  i*9;
                Math.acos(b);
                Math.sin(b);
                Math.acos(b);
                Math.sin(b);
                Math.acos(b);
                Math.sin(b);
                Math.acos(b);
                Math.sin(b);
            }
        });
    }
    long time  = System.currentTimeMillis();
    int count = 10000000;
    while(count>0) {
        es.execute(new Runnable() {
                
            int i = 9;
            @Override
            public void run() {
                int b =  i*9;
                Math.acos(b);
                Math.sin(b);
                Math.acos(b);
                Math.sin(b);
                Math.acos(b);
                Math.sin(b);
                Math.acos(b);
                Math.sin(b);
            }
        });
        count--;
    }
    es.shutdown();
    System.out.println(System.currentTimeMillis() - time);
        
}

自定義線程池的方式:

public static void SelfThreadTest() {
    final ConcurrentLinkedQueue<Integer> queue1 = new ConcurrentLinkedQueue<>();
        
    final List<Thread> threadlist = new ArrayList<>(100);
    for(int i = 0;i<100;i++) {
        Thread t = new Thread(new Runnable() {
                
            Integer b ;
            boolean next = true;
            @Override
            public void run() {
                while(true) {
                    b = queue1.poll();
                    if(b == null) {
                        if(!next) {return;}
                        continue;
                    }
                    int c = b*9;
                    Math.acos(b);
                    Math.sin(b);
                    Math.acos(b);
                    Math.sin(b);
                    Math.acos(b);
                    Math.sin(b);
                    Math.acos(b);
                    Math.sin(b);
                    next = false;
                }
                    
            }
        });
            
        threadlist.add(t);
    }
        
        
    for(int i = 0;i<100;i++) {
        threadlist.get(i).start();
    }
        
    int count = 10000000;
    long time  = System.currentTimeMillis();
    while(count>0) {
        queue1.offer(count);
        count --;
            
    }

    while(true) {
        if(threadlist.get(99).getState() == Thread.State.TERMINATED) {  // 這里沒有做太詳細(xì)的判斷略
            break;
        }
    }
        
    System.out.println(System.currentTimeMillis() - time);
}

這兩個(gè)某次運(yùn)行結(jié)果為:

begin
5302
4347
end

第一行為ExecutorService的運(yùn)行結(jié)果,第二行為自定義線程池的運(yùn)行結(jié)果。每次的運(yùn)行時(shí)間都會(huì)有所差別,但是結(jié)果都是類似的。
是的,時(shí)間還是有挺大差別的,但是這個(gè)差別并不是上次說的" 后續(xù)會(huì)重復(fù)創(chuàng)建線程消耗的時(shí)間” 。因?yàn)楹罄m(xù)是不繼續(xù)生成線程的,ExecutorService中的一個(gè)線程其實(shí)在內(nèi)部也是不停的循環(huán),等待Runnable過來。而這個(gè)Runnable也僅是個(gè)接口實(shí)現(xiàn)了run方法,Thread循環(huán)取這個(gè)run方法進(jìn)行運(yùn)行。而這里的時(shí)間差別應(yīng)該是ExecutorService中的各種線程檢查和判斷損失的時(shí)間(僅猜測(cè)-我們后續(xù)文章一起分析源碼)。至于選擇,大家還是先擇系統(tǒng)的比較好,畢竟可以保證結(jié)果的穩(wěn)定。自定義的方式,大家可以研究研究,如果要用在生產(chǎn)環(huán)境上,務(wù)必需要進(jìn)行足夠量的測(cè)試。

如果對(duì)Java線程創(chuàng)建流程感興趣,可以參考這篇文章:https://yq.aliyun.com/articles/72803 。文章中講到Java的線程創(chuàng)建最終會(huì)通過JNI調(diào)用到系統(tǒng)的pthread_create(C語言線程創(chuàng)建),可見Java的線程就是真實(shí)的線程。

最后編輯于
?著作權(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),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 前言 多線程并發(fā)編程是Java編程中重要的一塊內(nèi)容,也是面試重點(diǎn)覆蓋區(qū)域,所以學(xué)好多線程并發(fā)編程對(duì)我們來說極其重要...
    嘟爺MD閱讀 7,416評(píng)論 21 272
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,590評(píng)論 19 139
  • 先看幾個(gè)概念:線程:進(jìn)程中負(fù)責(zé)程序執(zhí)行的執(zhí)行單元。一個(gè)進(jìn)程中至少有一個(gè)線程。多線程:解決多任務(wù)同時(shí)執(zhí)行的需求,合理...
    yeying12321閱讀 614評(píng)論 0 0
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,734評(píng)論 18 399
  • 耶和華神所造的,惟有蛇比田野一切的活物更狡猾。蛇對(duì)女人說:“神豈是真說不許你們吃園中所有樹上的果子嗎?”女人對(duì)蛇說...
    OnlywantyouJesu閱讀 674評(píng)論 0 0

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