Java基礎(chǔ)26-Java線程池解析

概述

多線程技術(shù)主要解決處理器單元內(nèi)多個線程執(zhí)行的問題,它可以顯著減少處理器單元的閑置時間,增加處理器單元的吞吐能力。

假設(shè)一個服務(wù)器完成一項(xiàng)任務(wù)所需時間為:T1 創(chuàng)建線程時間,T2 在線程中執(zhí)行任務(wù)的時間,T3 銷毀線程時間。
如果:T1 + T3 遠(yuǎn)大于 T2,則可以采用線程池,以提高服務(wù)器性能。
一個線程池包括以下四個基本組成部分:

  • 線程池管理器(ThreadPool):用于創(chuàng)建并管理線程池,包括 創(chuàng)建線程池,銷毀線程池,添加新任務(wù);
  • 工作線程(PoolWorker):線程池中線程,在沒有任務(wù)時處于等待狀態(tài),可以循環(huán)的執(zhí)行任務(wù);
  • 任務(wù)接口(Task):每個任務(wù)必須實(shí)現(xiàn)的接口,以供工作線程調(diào)度任務(wù)的執(zhí)行,它主要規(guī)定了任務(wù)的入口,任務(wù)執(zhí)行完后的收尾工作,任務(wù)的執(zhí)行狀態(tài)等;
  • 任務(wù)隊(duì)列(taskQueue):用于存放沒有處理的任務(wù)。提供一種緩沖機(jī)制。

線程池技術(shù)正是關(guān)注如何縮短或調(diào)整T1,T3時間的技術(shù),從而提高服務(wù)器程序性能的。
它把T1,T3分別安排在服務(wù)器程序的啟動和結(jié)束的時間段或者一些空閑的時間段,這樣在服務(wù)器程序處理客戶請求時,不會有T1,T3的開銷了。

線程池不僅調(diào)整T1,T3產(chǎn)生的時間段,而且它還顯著減少了創(chuàng)建線程的數(shù)目,看一個例子:
假設(shè)一個服務(wù)器一天要處理50000個請求,并且每個請求需要一個單獨(dú)的線程完成。在線程池中,線程數(shù)一般是固定的,所以產(chǎn)生線程總數(shù)不會超過線程池中線程的數(shù)目,而如果服務(wù)器不利用線程池來處理這些請求則線程總數(shù)為50000。一般線程池大小是遠(yuǎn)小于50000。所以利用線程池的服務(wù)器程序不會為了創(chuàng)建50000而在處理請求時浪費(fèi)時間,從而提高效率。

線程的使用在java中占有極其重要的地位,在jdk1.4極其之前的jdk版本中,關(guān)于線程池的使用是極其簡陋的。在jdk1.5之后這一情況有了很大的改觀。Jdk1.5之后加入了java.util.concurrent包,這個包中主要介紹java中線程以及線程池的使用。為我們在開發(fā)中處理線程的問題提供了非常大的幫助。

為什么要使用線程池

  • 1.創(chuàng)建/銷毀線程伴隨著系統(tǒng)開銷,過于頻繁的創(chuàng)建/銷毀線程,會很大程度上影響處-理效率
  • 2.可以根據(jù)系統(tǒng)的承受能力,調(diào)整線程池中工作線線程的數(shù)目,防止因?yàn)橄倪^多的內(nèi)存,而把服務(wù)器累趴下(每個線程需要大約1MB內(nèi)存,線程開的越多,消耗的內(nèi)存也就越大,最后死機(jī))
  • 3.對線程進(jìn)行一些簡單的管理

ThreadPoolExecutor

Java里面線程池的頂級接口是Executor,但是嚴(yán)格意義上講Executor并不是一個線程池,而只是一個執(zhí)行線程的工具。

真正的線程池接口是ExecutorService。即:ExecutorService接口是Executor的子接口,而ThreadPoolExecutor實(shí)現(xiàn)了ExecutorService,是具體的實(shí)現(xiàn)類:

學(xué)習(xí)Java中的線程池,就可以直接學(xué)習(xí)ThreadPoolExecutor,對線程池的配置就是對ThreadPoolExecutor構(gòu)造函數(shù)的參數(shù)的配置:

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             threadFactory, defaultHandler);
    }
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }
    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.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

各個參數(shù)的含義:

  • int corePoolSize:該線程池中核心線程數(shù)最大值
    核心線程:線程池新建線程的時候,如果當(dāng)前線程總數(shù)小于corePoolSize,則新建的是核心線程,如果超過corePoolSize,則新建的是非核心線程,核心線程默認(rèn)情況下會一直存活在線程池中,即使這個核心線程啥也不干(閑置狀態(tài))。
    如果指定ThreadPoolExecutor的allowCoreThreadTimeOut這個屬性為true,那么核心線程如果不干活(閑置狀態(tài))的話,超過一定時間(時長下面參數(shù)決定),就會被銷毀掉。
  • int maximumPoolSize: 該線程池中線程總數(shù)最大值
    線程總數(shù) = 核心線程數(shù) + 非核心線程數(shù)。
  • long keepAliveTime:該線程池中非核心線程閑置超時時長
    一個非核心線程,如果不干活(閑置狀態(tài))的時長超過這個參數(shù)所設(shè)定的時長,就會被銷毀掉,如果設(shè)置allowCoreThreadTimeOut = true,則同樣會作用于核心線程。
  • TimeUnit unit:keepAliveTime的單位
    TimeUnit是一個枚舉類型,其包括:
NANOSECONDS : 1微毫秒 = 1微秒 / 1000
MICROSECONDS : 1微秒 = 1毫秒 / 1000
MILLISECONDS : 1毫秒 = 1秒 /1000
SECONDS : 秒
MINUTES : 分
HOURS : 小時
DAYS : 天
  • BlockingQueue workQueue:該線程池中的任務(wù)隊(duì)列:維護(hù)著等待執(zhí)行的Runnable對象
    當(dāng)所有的核心線程都在干活時,新添加的任務(wù)會被添加到這個隊(duì)列中等待處理,如果隊(duì)列滿了,則新建非核心線程執(zhí)行任務(wù)。
    常用的workQueue類型:
    1.SynchronousQueue
    這個隊(duì)列接收到任務(wù)的時候,會直接提交給線程處理,而不保留它。
    如果所有線程都在工作怎么辦?
    那就新建一個線程來處理這個任務(wù)!所以為了保證不出現(xiàn)<線程數(shù)達(dá)到了maximumPoolSize而不能新建線程>的錯誤,使用這個類型隊(duì)列的時候,maximumPoolSize一般指定成Integer.MAX_VALUE,即無限大。
    2.LinkedBlockingQueue
    這個隊(duì)列接收到任務(wù)的時候,如果當(dāng)前線程數(shù)小于核心線程數(shù),則新建線程(核心線程)處理任務(wù);如果當(dāng)前線程數(shù)等于核心線程數(shù),則進(jìn)入隊(duì)列等待。由于這個隊(duì)列默認(rèn)大小為Integer.MAX_VALUE,即所有超過核心線程數(shù)的任務(wù)都將被添加到隊(duì)列中,所以如果未通過構(gòu)造方法設(shè)置隊(duì)列長度就會導(dǎo)致maximumPoolSize的設(shè)定失效,因?yàn)榭偩€程數(shù)永遠(yuǎn)不會超過corePoolSize。
    3.ArrayBlockingQueue
    可以限定隊(duì)列的長度,接收到任務(wù)的時候,如果沒有達(dá)到corePoolSize的值,則新建線程(核心線程)執(zhí)行任務(wù),如果達(dá)到了,則入隊(duì)等候,如果隊(duì)列已滿,則新建線程(非核心線程)執(zhí)行任務(wù),又如果總線程數(shù)到了maximumPoolSize,并且隊(duì)列也滿了,則發(fā)生錯誤。
    4.DelayQueue
    隊(duì)列內(nèi)元素必須實(shí)現(xiàn)Delayed接口,這就意味著你傳進(jìn)去的任務(wù)必須先實(shí)現(xiàn)Delayed接口。這個隊(duì)列接收到任務(wù)時,首先先入隊(duì),只有達(dá)到了指定的延時時間,才會執(zhí)行任務(wù)。

如何向ThreadPoolExecutor添加任務(wù)

通過ThreadPoolExecutor.execute(Runnable command)方法即可向線程池內(nèi)添加一個任務(wù)。

    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

RejectedExecutionHandler拒絕策略

RejectedExecutionHandler是一個接口,當(dāng)要創(chuàng)建的線程數(shù)量大于線程池的最大線程數(shù)的時候,新的任務(wù)就會被拒絕,就會調(diào)用這個接口里的rejectedExecution方法:

package java.util.concurrent;

/**
 * A handler for tasks that cannot be executed by a {@link ThreadPoolExecutor}.
 *
 * @since 1.5
 * @author Doug Lea
 */
public interface RejectedExecutionHandler {

    /**
     * Method that may be invoked by a {@link ThreadPoolExecutor} when
     * {@link ThreadPoolExecutor#execute execute} cannot accept a
     * task.  This may occur when no more threads or queue slots are
     * available because their bounds would be exceeded, or upon
     * shutdown of the Executor.
     *
     * <p>In the absence of other alternatives, the method may throw
     * an unchecked {@link RejectedExecutionException}, which will be
     * propagated to the caller of {@code execute}.
     *
     * @param r the runnable task requested to be executed
     * @param executor the executor attempting to execute this task
     * @throws RejectedExecutionException if there is no remedy
     */
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}

Java中為我們提供了4種拒絕策略:

  • AbortPolicy
    ThreadPoolExecutor中默認(rèn)的拒絕策略就是AbortPolicy,直接拋出異常。
    源碼如下:
    /**
     * A handler for rejected tasks that throws a
     * {@code RejectedExecutionException}.
     */
    public static class AbortPolicy implements RejectedExecutionHandler {
        /**
         * Creates an {@code AbortPolicy}.
         */
        public AbortPolicy() { }

        /**
         * Always throws RejectedExecutionException.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         * @throws RejectedExecutionException always
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }
    }

簡單粗暴,直接拋出個RejectedExecutionException異常,也不執(zhí)行這個任務(wù)了。

  • CallerRunsPolicy
    CallerRunsPolicy在任務(wù)被拒絕添加后,會調(diào)用當(dāng)前線程池的所在的線程去執(zhí)行被拒絕的任務(wù)。
    源碼如下:
    /**
     * A handler for rejected tasks that runs the rejected task
     * directly in the calling thread of the {@code execute} method,
     * unless the executor has been shut down, in which case the task
     * is discarded.
     */
    public static class CallerRunsPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code CallerRunsPolicy}.
         */
        public CallerRunsPolicy() { }

        /**
         * Executes task r in the caller's thread, unless the executor
         * has been shut down, in which case the task is discarded.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }
    }
  • DiscardPolicy
    采用這個拒絕策略,會讓被線程池拒絕的任務(wù)直接拋棄,不會拋異常也不會執(zhí)行。
    源碼如下:
    /**
     * A handler for rejected tasks that silently discards the
     * rejected task.
     */
    public static class DiscardPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code DiscardPolicy}.
         */
        public DiscardPolicy() { }

        /**
         * Does nothing, which has the effect of discarding task r.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        }
    }
  • DiscardOldestPolicy
    DiscardOldestPolicy策略的作用是:當(dāng)任務(wù)被拒絕添加時,會拋棄任務(wù)隊(duì)列中最舊的任務(wù)也就是最先加入隊(duì)列的,再把這個新任務(wù)添加進(jìn)去。
    源碼如下:
    /**
     * A handler for rejected tasks that discards the oldest unhandled
     * request and then retries {@code execute}, unless the executor
     * is shut down, in which case the task is discarded.
     */
    public static class DiscardOldestPolicy implements RejectedExecutionHandler {
        /**
         * Creates a {@code DiscardOldestPolicy} for the given executor.
         */
        public DiscardOldestPolicy() { }

        /**
         * Obtains and ignores the next task that the executor
         * would otherwise execute, if one is immediately available,
         * and then retries execution of task r, unless the executor
         * is shut down, in which case task r is instead discarded.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                e.getQueue().poll();
                e.execute(r);
            }
        }
    }

當(dāng)然我們也可以自定義拒絕策略,只需要實(shí)現(xiàn)RejectedExecutionHandler接口并重寫rejectedExecution方法即可,例如讓被拒絕的任務(wù)在一個新的線程中執(zhí)行:

static class MyRejectedExecutionHandler implements RejectedExecutionHandler {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        new Thread(r,"新線程"+new Random().nextInt(10)).start();
    }
}

Java自帶的四種線程池

如果你不想自己寫一個線程池,Java通過Executors這個類提供了四種線程池,這四種線程池都是直接或間接配置ThreadPoolExecutor的參數(shù)實(shí)現(xiàn)的。

  • 1.CachedThreadPool:可緩存線程池
    /**
     * Creates a thread pool that creates new threads as needed, but
     * will reuse previously constructed threads when they are
     * available.  These pools will typically improve the performance
     * of programs that execute many short-lived asynchronous tasks.
     * Calls to {@code execute} will reuse previously constructed
     * threads if available. If no existing thread is available, a new
     * thread will be created and added to the pool. Threads that have
     * not been used for sixty seconds are terminated and removed from
     * the cache. Thus, a pool that remains idle for long enough will
     * not consume any resources. Note that pools with similar
     * properties but different details (for example, timeout parameters)
     * may be created using {@link ThreadPoolExecutor} constructors.
     *
     * @return the newly created thread pool
     */
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

    /**
     * Creates a thread pool that creates new threads as needed, but
     * will reuse previously constructed threads when they are
     * available, and uses the provided
     * ThreadFactory to create new threads when needed.
     * @param threadFactory the factory to use when creating new threads
     * @return the newly created thread pool
     * @throws NullPointerException if threadFactory is null
     */
    public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }

根據(jù)源碼可以看出:
1.這種線程池內(nèi)部沒有核心線程,線程的數(shù)量是有沒限制的。
2.在創(chuàng)建任務(wù)時,若有空閑的線程時則復(fù)用空閑的線程,若沒有則新建線程。
3.沒有工作的線程(閑置狀態(tài))在超過了60S還不做事,就會銷毀。

  • 2.FixedThreadPool:定長線程池
    /**
     * Creates a thread pool that reuses a fixed number of threads
     * operating off a shared unbounded queue.  At any point, at most
     * {@code nThreads} threads will be active processing tasks.
     * If additional tasks are submitted when all threads are active,
     * they will wait in the queue until a thread is available.
     * If any thread terminates due to a failure during execution
     * prior to shutdown, a new one will take its place if needed to
     * execute subsequent tasks.  The threads in the pool will exist
     * until it is explicitly {@link ExecutorService#shutdown shutdown}.
     *
     * @param nThreads the number of threads in the pool
     * @return the newly created thread pool
     * @throws IllegalArgumentException if {@code nThreads <= 0}
     */
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

    /**
     * Creates a thread pool that reuses a fixed number of threads
     * operating off a shared unbounded queue, using the provided
     * ThreadFactory to create new threads when needed.  At any point,
     * at most {@code nThreads} threads will be active processing
     * tasks.  If additional tasks are submitted when all threads are
     * active, they will wait in the queue until a thread is
     * available.  If any thread terminates due to a failure during
     * execution prior to shutdown, a new one will take its place if
     * needed to execute subsequent tasks.  The threads in the pool will
     * exist until it is explicitly {@link ExecutorService#shutdown
     * shutdown}.
     *
     * @param nThreads the number of threads in the pool
     * @param threadFactory the factory to use when creating new threads
     * @return the newly created thread pool
     * @throws NullPointerException if threadFactory is null
     * @throws IllegalArgumentException if {@code nThreads <= 0}
     */
    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }

根據(jù)源碼可以看出:
1.該線程池的最大線程數(shù)等于核心線程數(shù),所以在默認(rèn)情況下,該線程池的線程不會因?yàn)殚e置狀態(tài)超時而被銷毀。
2.如果當(dāng)前線程數(shù)小于核心線程數(shù),并且也有閑置線程的時候提交了任務(wù),這時也不會去復(fù)用之前的閑置線程,會創(chuàng)建新的線程去執(zhí)行任務(wù)。如果當(dāng)前執(zhí)行任務(wù)數(shù)大于了核心線程數(shù),大于的部分就會進(jìn)入隊(duì)列等待。等著有閑置的線程來執(zhí)行這個任務(wù)。

  • SingleThreadPool單線程線程池
    /**
     * Creates an Executor that uses a single worker thread operating
     * off an unbounded queue. (Note however that if this single
     * thread terminates due to a failure during execution prior to
     * shutdown, a new one will take its place if needed to execute
     * subsequent tasks.)  Tasks are guaranteed to execute
     * sequentially, and no more than one task will be active at any
     * given time. Unlike the otherwise equivalent
     * {@code newFixedThreadPool(1)} the returned executor is
     * guaranteed not to be reconfigurable to use additional threads.
     *
     * @return the newly created single-threaded Executor
     */
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

    /**
     * Creates an Executor that uses a single worker thread operating
     * off an unbounded queue, and uses the provided ThreadFactory to
     * create a new thread when needed. Unlike the otherwise
     * equivalent {@code newFixedThreadPool(1, threadFactory)} the
     * returned executor is guaranteed not to be reconfigurable to use
     * additional threads.
     *
     * @param threadFactory the factory to use when creating new
     * threads
     *
     * @return the newly created single-threaded Executor
     * @throws NullPointerException if threadFactory is null
     */
    public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }

根據(jù)源碼可以看出:
1.有且僅有一個工作線程執(zhí)行任務(wù)
2.所有任務(wù)按照指定順序執(zhí)行,即遵循隊(duì)列的入隊(duì)出隊(duì)規(guī)則

  • ScheduledThreadPool延期周期執(zhí)行線程池
    /**
     * Creates a thread pool that can schedule commands to run after a
     * given delay, or to execute periodically.
     * @param corePoolSize the number of threads to keep in the pool,
     * even if they are idle
     * @return a newly created scheduled thread pool
     * @throws IllegalArgumentException if {@code corePoolSize < 0}
     */
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

    /**
     * Creates a thread pool that can schedule commands to run after a
     * given delay, or to execute periodically.
     * @param corePoolSize the number of threads to keep in the pool,
     * even if they are idle
     * @param threadFactory the factory to use when the executor
     * creates a new thread
     * @return a newly created scheduled thread pool
     * @throws IllegalArgumentException if {@code corePoolSize < 0}
     * @throws NullPointerException if threadFactory is null
     */
    public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }

根據(jù)源碼可以看出:
DEFAULT_KEEPALIVE_MILLIS就是默認(rèn)10L,這里就是10秒。這個線程池有點(diǎn)像是吧CachedThreadPool和FixedThreadPool 結(jié)合了一下。
不僅設(shè)置了核心線程數(shù),最大線程數(shù)也是Integer.MAX_VALUE。
這個線程池是上述4個中為唯一個有延遲執(zhí)行和周期執(zhí)行任務(wù)的線程池。

線程池的執(zhí)行流程

  • 新任務(wù)進(jìn)來先看是否有可用核心線程,如果有,則使用核心線程處理此任務(wù)
  • 如果核心線程數(shù)已滿,則添加到任務(wù)隊(duì)列等待處理
  • 如果任務(wù)隊(duì)列已滿,且核心線程數(shù)已滿,則使用非核心線程處理任務(wù)
  • 如果隊(duì)列已滿,核心線程和非核心線程都已滿,則執(zhí)行拒絕策略

線程池的關(guān)閉

ThreadPoolExecutor提供了兩個方法,用于線程池的關(guān)閉,分別是shutdown()和shutdownNow(),其中:

  • shutdown():不會立即終止線程池,而是要等所有任務(wù)緩存隊(duì)列中的任務(wù)都執(zhí)行完后才終止,但再也不會接受新的任務(wù)
  • shutdownNow():立即終止線程池,并嘗試打斷正在執(zhí)行的任務(wù),并且清空任務(wù)緩存隊(duì)列,返回尚未執(zhí)行的任務(wù)

如何選擇線程池數(shù)量

線程池的大小決定著系統(tǒng)的性能,過大或者過小的線程池數(shù)量都無法發(fā)揮最優(yōu)的系統(tǒng)性能。

當(dāng)然線程池的大小也不需要做的太過于精確,只需要避免過大和過小的情況。一般來說,確定線程池的大小需要考慮CPU的數(shù)量,內(nèi)存大小,任務(wù)是計算密集型還是IO密集型等因素。

NCPU = CPU的數(shù)量
UCPU = 期望對CPU的使用率 0 ≤ UCPU ≤ 1
W/C = 等待時間與計算時間的比率

如果希望處理器達(dá)到理想的使用率,那么線程池的最優(yōu)大小為:
線程池大小=NCPU * UCPU(1+W/C)

在Java中使用int ncpus = Runtime.getRuntime().availableProcessors();可以獲取CPU的數(shù)量。

線程池工廠

Executors的線程池如果不指定線程工廠會使用Executors中的DefaultThreadFactory
默認(rèn)線程池工廠創(chuàng)建的線程都是非守護(hù)線程。
使用自定義的線程工廠可以做很多事情,比如可以跟蹤線程池在何時創(chuàng)建了多少線程,也可以自定義線程名稱和優(yōu)先級。如果將
新建的線程都設(shè)置成守護(hù)線程,當(dāng)主線程退出后,將會強(qiáng)制銷毀線程池。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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