Java--線程池

Java中提供了一系列和鎖相關(guān)的工具:synchronized茉唉、ReentrantLock乒验、AtomicXXX類吩翻,它們都能幫助解決我們進(jìn)行多線程操作時(shí)并發(fā)問題海蔽,之前我們也提到了內(nèi)核線程模型,Hotspot虛擬機(jī)線程的創(chuàng)建都是通過內(nèi)核線程提供的輕量級進(jìn)程API創(chuàng)建的输硝,反復(fù)的創(chuàng)建線程會(huì)不斷的由OS線程調(diào)度今瀑,這也會(huì)消耗大量CPU性能
爭對創(chuàng)建線程,除了使用Thread之外点把,Java還提供了線程池類橘荠,它們最終都實(shí)現(xiàn)的Executor接口,JDK1.7中的繼承實(shí)現(xiàn)關(guān)系如下:

一郎逃、使用線程池與Thread對比

使用Thread創(chuàng)建10000個(gè)線程哥童,并運(yùn)行,記錄時(shí)間:

public class ThreadPoolTest {

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        List<Thread> threads = new ArrayList<>(10000);

        for (int i = 0; i < 10000; i++) {
            Thread thread = new Thread();
            threads.add(thread);
            thread.start();
        }

        for(Thread t:threads){
            try {
                t.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println("ms:"+(System.currentTimeMillis() - startTime));
    }

}

結(jié)果:
ms:1761

使用線程池:

public class ThreadPoolTest2 {

    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        int size = 10000;

        ExecutorService executorService = Executors.newSingleThreadExecutor();
        final CountDownLatch countDownLatch = new CountDownLatch(size);

        for (int i = 0; i < size; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    countDownLatch.countDown();
                }
            });
        }

        try {
            countDownLatch.await();//等待10000計(jì)數(shù)結(jié)束
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        executorService.shutdown();
        System.out.println("ms:" + (System.currentTimeMillis() - startTime));
    }

}

結(jié)果:
ms:69

兩者相差的時(shí)間為100倍以上褒翰,這邊使用的線程池回頭再來看贮懈,由這個(gè)例子可以看出,線程的創(chuàng)建到運(yùn)行過程优训,會(huì)花費(fèi)大量的cpu時(shí)間朵你,爭對需要大量并發(fā)的操作,使用線程池可以大大提高程序性能

二揣非、ThreadPoolExecutor

1.ThreadPoolExecutor執(zhí)行任務(wù)圖解

Executors是個(gè)幫助類撬呢,內(nèi)部實(shí)際使用的是ThreadPoolExecutor,構(gòu)造函數(shù)如下:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

ThreadPoolExecutor大致的執(zhí)行場景如下:


execute方法執(zhí)行流程
2.ThreadPoolExecutor參數(shù)

再來看構(gòu)造函數(shù)中各個(gè)參數(shù)的含義:

  • corePoolSize:核心線程最大數(shù)妆兑,當(dāng)有新任務(wù)時(shí)魂拦,如果小于該值,那么會(huì)直接創(chuàng)建線程或使用空閑的核心線程執(zhí)行任務(wù)

  • maximumPoolSize:最大線程數(shù)搁嗓,需大于核心線程數(shù)芯勘,非核心線程最大數(shù) = 最大線程數(shù) - 核心線程最大數(shù),當(dāng)核心線程全部創(chuàng)建并都在執(zhí)行任務(wù)腺逛,那么新任務(wù)使用非核心線程執(zhí)行荷愕,同樣使用核心線程創(chuàng)建機(jī)制:創(chuàng)建線程或使用空閑的線程,非核心線程可以指定死亡時(shí)間棍矛,相當(dāng)于臨時(shí)工

  • keepAliveTime:非核心線程的保持存活時(shí)間安疗,當(dāng)一個(gè)非核心線程持續(xù)keepAliveTime時(shí)間都處于空閑狀態(tài),那么使它消亡

  • unit:保持存活時(shí)間的時(shí)間單位

  • workQueue:任務(wù)隊(duì)列够委,當(dāng)線程總數(shù)到達(dá)最大荐类,并且所有線程都有任務(wù)在執(zhí)行,那么新任務(wù)可以進(jìn)入該隊(duì)列茁帽,等待有線程空閑時(shí)執(zhí)行隊(duì)列中的任務(wù)玉罐。通過Executors創(chuàng)建的任務(wù)隊(duì)列屈嗤,都沒有最大值,所以當(dāng)遇到服務(wù)器并發(fā)量大的情況吊输,容易OOM饶号,安卓開發(fā)中不需要考慮這個(gè)問題

  • threadFactory:創(chuàng)建線程工廠,一般使用默認(rèn)的即可

  • handler:拒絕策略季蚂,當(dāng)觸發(fā)拒絕時(shí)茫船,執(zhí)行該對象策略策略

3.任務(wù)隊(duì)列

任務(wù)隊(duì)列實(shí)現(xiàn)BlockingQueue接口,JDK提供了以下4種:

  • ArrayBlockingQueue: 是一個(gè)基于數(shù)組結(jié)構(gòu)的有界阻塞隊(duì)列扭屁,此隊(duì)列按 FIFO(先進(jìn)先出)原則對元素進(jìn)行排序算谈。
  • LinkedBlockingQueue: 一個(gè)基于鏈表結(jié)構(gòu)的阻塞隊(duì)列,此隊(duì)列按FIFO(先進(jìn)先出)排序元素疯搅,吞吐量通常要高于ArrayBlockingQueue濒生。靜態(tài)工廠方法Executors.newFixedThreadPool()使用了這個(gè)隊(duì)列埋泵。
  • SynchronousQueue: 一個(gè)不存儲元素的阻塞隊(duì)列幔欧。每個(gè)插入操作必須等到另一個(gè)線程調(diào)用移除操作,否則插入操作一直處于阻塞狀態(tài)丽声,吞吐量通常要高于LinkedBlockingQueue礁蔗,靜態(tài)工廠方法Executors.newCachedThreadPool使用了這個(gè)隊(duì)列。
  • PriorityBlockingQueue: 一個(gè)具有優(yōu)先級的無限阻塞隊(duì)列雁社。
4.拒絕策略
  • DiscardPolicy:丟棄當(dāng)前將要加入隊(duì)列的任務(wù)本身浴井。
  • DiscardOldestPolicy:丟棄任務(wù)隊(duì)列中最舊任務(wù)。(丟棄最舊任務(wù)也不是簡單的丟棄最舊的任務(wù)霉撵,而是有一些額外的處理)
  • AbortPolicy:拋出異常的方式磺浙。
  • CallerRunsPolicy:不進(jìn)入線程池執(zhí)行,由調(diào)用者線程執(zhí)行徒坡。

三撕氧、Executors

后臺一般不允許使用Executors,但我們安卓開發(fā)是可以使用的喇完,接下來來使用Executors三種線程池伦泥,有了上面ThreadPoolExecutor的了解,理解起來很簡單

public class ThreadPoolTest3 {

    public static void main(String[] args) {
        ExecutorService executorService1 = Executors.newCachedThreadPool();
        ExecutorService executorService2 = Executors.newFixedThreadPool(10);
        ExecutorService executorService3 = Executors.newSingleThreadExecutor();
        for (int i = 1; i <= 100; i++) {
            executorService1.execute(new TestRunnable(i));
//            executorService2.execute(new TestRunnable(i));
//            executorService3.execute(new TestRunnable(i));
        }
    }

    static class TestRunnable implements Runnable {
        int i = 0;

        public TestRunnable(int i) {
            this.i = i;
        }


        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + ":" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}
1.newCachedThreadPool

newCachedThreadPool方法如下:

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

它和直接創(chuàng)建線程類似锦溪,可以創(chuàng)建無限的線程

2.newFixedThreadPool

newFixedThreadPool方法如下:

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

它擁有指定大小的核心線程數(shù)不脯,并且只有核心線程,以及一個(gè)無限大小的任務(wù)隊(duì)列

3.newSingleThreadExecutor

newSingleThreadExecutor方法如下:

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

它僅有一個(gè)核心線程刻诊,以及一個(gè)無限大小的任務(wù)隊(duì)列防楷,所以相當(dāng)于單線程執(zhí)行任務(wù)隊(duì)列中的任務(wù)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市则涯,隨后出現(xiàn)的幾起案子域帐,更是在濱河造成了極大的恐慌赘被,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件肖揣,死亡現(xiàn)場離奇詭異民假,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)龙优,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進(jìn)店門羊异,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人彤断,你說我怎么就攤上這事野舶。” “怎么了宰衙?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵平道,是天一觀的道長。 經(jīng)常有香客問我供炼,道長一屋,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任袋哼,我火速辦了婚禮冀墨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘涛贯。我一直安慰自己诽嘉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布弟翘。 她就那樣靜靜地躺著虫腋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪稀余。 梳的紋絲不亂的頭發(fā)上悦冀,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天,我揣著相機(jī)與錄音滚躯,去河邊找鬼雏门。 笑死,一個(gè)胖子當(dāng)著我的面吹牛掸掏,可吹牛的內(nèi)容都是我干的茁影。 我是一名探鬼主播,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼丧凤,長吁一口氣:“原來是場噩夢啊……” “哼募闲!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起愿待,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤浩螺,失蹤者是張志新(化名)和其女友劉穎靴患,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體要出,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鸳君,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了患蹂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片或颊。...
    茶點(diǎn)故事閱讀 40,567評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖传于,靈堂內(nèi)的尸體忽然破棺而出囱挑,到底是詐尸還是另有隱情,我是刑警寧澤沼溜,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布平挑,位于F島的核電站,受9級特大地震影響系草,放射性物質(zhì)發(fā)生泄漏通熄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一悄但、第九天 我趴在偏房一處隱蔽的房頂上張望棠隐。 院中可真熱鬧石抡,春花似錦檐嚣、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至隐解,卻和暖如春鞍帝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背煞茫。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工帕涌, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人续徽。 一個(gè)月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓蚓曼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親钦扭。 傳聞我的和親對象是個(gè)殘疾皇子纫版,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評論 2 359

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