Java多線程ThreadPoolExecutor

作用

一场勤、使用簡單
二们拙、方便管理多線程
三锡移、可重復利用線程

一個例子

通過下面這個例子,可以看出線程池使用起來非常簡單崔赌、方便

public class StartProvider {
    public static void main(String[] args) {
        ExecutorService threadPoolExecutor = Executors.newFixedThreadPool(2);
        for (int i = 1; i < 7; ++i) {
            threadPoolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("test");
                }
            });
        }
    }
}

Executors的newFixedThreadPool是創(chuàng)建一個大小固定意蛀,其它參數(shù)默認的線程池耸别,然后執(zhí)行Runnable任務,使用起來簡單县钥,下面是線程池的參數(shù):

public ThreadPoolExecutor(int corePoolSize,  int maximumPoolSize, 
  long keepAliveTime,  TimeUnit unit,   BlockingQueue<Runnable> workQueue, 
  ThreadFactory threadFactory, RejectedExecutionHandler handler) 
  • corePoolSize: 核心線程數(shù)(常駐線程池的線程數(shù))
  • maximumPoolSize: 線程池中最大線程數(shù)目
  • keepAliveTime: 池中線程數(shù)多于核心線程并且idle時秀姐,多于核心線程的其它線程存活時間
  • unit: keepAliveTime時間單位
  • workQueue: 存放任務的隊列(核心線程滿了之后,任務先進隊列)
  • threadFactory: 創(chuàng)建線程的線程工廠
  • handler: 線程池處理拋棄任務策略

參數(shù)設置

根據(jù)任務和運行的環(huán)境情況(cpu型若贮,io型省有,cpu核數(shù)等),設置參數(shù)的值谴麦,提高硬件的利用率
corePoolSize
核心線程數(shù)的大小蠢沿,在線程池初始化完成后,池中的線程個數(shù)為0匾效,每添加一個任務舷蟀,線程池就增加一個線程直到線程個數(shù)等于corePoolSize。對CPU繁忙型任務線程池大小(CPU核數(shù)為N)

線程池大小 = N + 1

對IO繁忙型任務線程池大小為

線程池大小 = (IO操作時間 + CPU運行時間)/CPU運行時間× N 或 2×N +1

maximumPoolSize
最大線程數(shù)弧轧,當workQueue已滿且線程池中的線程數(shù)少于maximumPoolSize時雪侥,如有新任務,就會創(chuàng)建新的線程精绎,一直到線程池中的線程大小為maximumPoolSize速缨。
keepAliveTime
idle線程(線程數(shù)大于corePoolSize且為idle)的存活時間,線程需要占用資源代乃,多余線程需要消亡旬牲,這些多余線程在將來很短時間內(nèi)也可能需要用到,keepAliveTime × unit是這些線程的存活時間搁吓。當任務數(shù)呈峰谷周期變化時原茅,keepAliveTime × unit設置為半周期,可以減少線程的創(chuàng)建和銷毀開銷堕仔。idle線程是存活keepAliveTime時間是通過取阻塞隊列里面的任務延時來實現(xiàn)的:

Runnable r = timed ? 
workQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS) : workQueue.take();

和keepAliveTime相對應的是allowCoreThreadTimeOut擂橘,表示線程池中的核心線程是否銷毀,線程池中的核心線程空閑較多時摩骨,通過設置allowCoreThreadTimeOut的值來表示是否銷毀idle的核心線程通贞。通過線程池的getActiveCount可以獲取線程池中正在執(zhí)行的線程數(shù)
workQueue
阻塞隊列,用于存儲任務恼五。常用的阻塞隊列有LinkedBlockingQueue昌罩、PriorityBlockingQueue和SynchronousQueue,它們分別用于不同的場景灾馒。LinkedBlockingQueue可以設置為有界阻塞隊列和無界阻塞隊列茎用,按先進先出的順序執(zhí)行任務;PriorityBlockingQueue優(yōu)先級隊列,根據(jù)Compatator比較器可以設置不同任務的執(zhí)行順序轨功;SynchronousQueue同步隊列旭斥,在插入一個任務進入隊列以后,需要取出這個任務才能繼續(xù)插入夯辖。
threadFactory
線程工廠琉预,繼承ThreadFactory可定制線程工廠
handler
任務隊列滿,且線程池中的線程數(shù)達到最大值(maximumPoolSize)蒿褂,有新任務到來時圆米,線程池用handler策略處理新任務。通過繼承RejectedExecutionHandler類可以定制處理策略啄栓,默認提供了三種處理策略:

  • CallerRunsPolicy: 在當前線程中執(zhí)行任務
public class Main {
    public static void main(String[] args){
        final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 2,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(1),
                Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());

        for (int i = 1; i <= 5; ++i) {
            final int index = i;
            threadPoolExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        if(!"main".equals(Thread.currentThread().getName())) {
                            Thread.sleep(1000);
                        }
                    } catch (InterruptedException e) {
                        System.out.println("nothing");
                    }
                    System.out.println("threadPoolInfo:poolSize = " + threadPoolExecutor.getPoolSize() + " queue size =" +  threadPoolExecutor.getQueue().size() +
                            ", currentTime:"  + System.currentTimeMillis() +  ", index = " + index + ", current thread name:" + Thread.currentThread().getName());
                }
            });
        }

        System.out.println("end");
    }
}

執(zhí)行結(jié)果



線程池任務超出負載時娄帖,任務由線程池所在的線程執(zhí)行,例子中的為main線程

  • AbortPolicy: 不執(zhí)行任務(默認策略)昙楚,并拋出RejectedExecutionException
public class Main {
    public static void main(String[] args){
        final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 2,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(1),
                Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

        for (int i = 1; i <= 12; ++i) {
            final int index = i;
            try {
                threadPoolExecutor.execute(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("threadPoolInfo:poolSize = " + threadPoolExecutor.getPoolSize() + " queue size =" + threadPoolExecutor.getQueue().size() +
                                ", currentTime:" + System.currentTimeMillis() + ", index = " + index + ", current thread name:" + Thread.currentThread().getName());
                    }
                });
            } catch (RejectedExecutionException e) {
                System.out.println("message : " + e.getMessage());
                try {
                    Thread.sleep(1000);
                } catch(InterruptedException e1) {
                    System.out.println("interrupted");
                }
            }
        }
        System.out.println("end");
    }
}

執(zhí)行結(jié)果



線程池任務超出負載時近速,沒有執(zhí)行任務,并拋出RejectExecutionException堪旧。拋出異常時削葱,需要捕獲異常,不然無法使用線程池淳梦。

  • DiscardPolicy:不執(zhí)行任務析砸,不拋出異常(靜默策略)
  • DiscardOldestPolicy : 拋棄阻塞隊列中最久的任務

一個問題

線程池是如何重復多次利用線程的?通過阻塞隊列爆袍,阻塞隊列中沒有任務時阻塞線程首繁,有任務則取出任務執(zhí)行,其原理和下面片段類似陨囊,但是復雜很多

public class Main {
    public static void main(String[] args) {
        final BlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<Runnable>();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        Runnable runnable = blockingQueue.take();
                        runnable.run();
                        System.out.println("blocking........");
                    } catch (InterruptedException e) {
                        System.out.println("interrupted");
                    }
                }
            }
        }).start();

        blockingQueue.add(new Runnable() {
            @Override
            public void run() {
                System.out.println("execute task!");
            }
        });
    }
}

線程池中線程線程需要銷毀則調(diào)用阻塞隊列的poll方法弦疮,設置阻塞超時時間;否則調(diào)用take方法阻塞

關閉

shutdown和shutdownNow未關閉線程池的兩個操作蜘醋,shutdown關閉idle線程胁塞,線程池里面的任務繼續(xù)執(zhí)行;shutdownNow不等待線程執(zhí)行完畢压语,直接關閉線程闲先。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市无蜂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蒙谓,老刑警劉巖斥季,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡酣倾,警方通過查閱死者的電腦和手機舵揭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來躁锡,“玉大人午绳,你說我怎么就攤上這事∮持” “怎么了拦焚?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長杠输。 經(jīng)常有香客問我赎败,道長,這世上最難降的妖魔是什么蠢甲? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任僵刮,我火速辦了婚禮,結(jié)果婚禮上鹦牛,老公的妹妹穿的比我還像新娘搞糕。我一直安慰自己,他們只是感情好曼追,可當我...
    茶點故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布窍仰。 她就那樣靜靜地躺著,像睡著了一般拉鹃。 火紅的嫁衣襯著肌膚如雪辈赋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天膏燕,我揣著相機與錄音钥屈,去河邊找鬼。 笑死坝辫,一個胖子當著我的面吹牛篷就,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼返弹,長吁一口氣:“原來是場噩夢啊……” “哼邦邦!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起未辆,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎锯玛,沒想到半個月后咐柜,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體兼蜈,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年拙友,在試婚紗的時候發(fā)現(xiàn)自己被綠了为狸。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡遗契,死狀恐怖辐棒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情牍蜂,我是刑警寧澤漾根,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站捷兰,受9級特大地震影響立叛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜贡茅,卻給世界環(huán)境...
    茶點故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一秘蛇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧顶考,春花似錦赁还、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至渊季,卻和暖如春朋蔫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背却汉。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工驯妄, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人合砂。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓青扔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親翩伪。 傳聞我的和親對象是個殘疾皇子微猖,可洞房花燭夜當晚...
    茶點故事閱讀 45,585評論 2 359

推薦閱讀更多精彩內(nèi)容