面試|newFixedThreadPool,newCachedThreadPool和newScheduledThreadPool的區別

前面主要分析ThreadPoolExecutor類的創建和運行過程,今天學習Executors類。

1.Executors類和Executor類的關係

  • Executor是含有執行提交Runnable任務的接口。如果你看了關於ThreadPoolExecutor類的分析,那麼就知道線程池間接實現Executor接口。
  • Executors是一個工廠類,它能提供各種形式的線程池。

2.Executors類的作用

創建以下線程池和執行器。

2.1 newFixedThreadPool

創建一個線程池,該線程池重用在共享無界隊列上運行的固定數量的線程。在任何時候,大多數nThreads線程都是活動的處理任務。如果在所有線程都處於活動狀態時提交其他任務,它們將在隊列中等待,直到線程可用爲止。如果任何線程在關閉之前的執行過程中由於失敗而終止,那麼如果需要執行後續任務,則會替換一個新線程。池中的線程將一直存在,直到顯式地執行execuorservice #shutdown關機。

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

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

注意看到核心線程數和最大線程數都爲nThreads,表明線程池內自始至終可用線程數爲nThread;如果線程池內線程數已經達到nThreads,那麼新到達的Runnable任務直接放入等待隊列,由於不會再新建線程,所以爲了保證池的可用性就使用無界等待隊列。
注意提供重載的方法,提供自定義的線程工廠。

2.2 newCachedThreadPool

創建一個線程池,該線程池根據需要創建新線程,但在可用時將重用以前構造的線程。這些池通常會提高執行許多短期異步任務的程序的性能。如果可用,要執行的調用將重用以前構造的線程。如果沒有可用的現有線程,將創建一個新線程並將其添加到池中。未使用60秒的線程將被終止並從緩存中刪除。因此,長時間空閒的池不會消耗任何資源。注意,可以使用ThreadPoolExecutor構造函數創建具有相似屬性但不同細節(例如超時參數)的池。

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

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

注意該線程池核心線程數爲0,最大線程池數爲2^32-1,空閒線程只存活60秒;等待隊列並不能存儲Runnable任務,反而使得線程池繼續創建線程來執行任務。

2.3 newScheduledThreadPool

創建一個線程池,該線程池可以調度在給定延遲之後運行的命令,或者定期執行命令。

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
}

    public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }

注意該線程池調用ScheduledThreadPoolExecutor類的構造方法。

2.4 newSingleThreadExecutor

創建一個執行器,該執行器使用一個工作線程操作一個無界隊列。(但是請注意,如果這個線程在關閉之前的執行過程中由於失敗而終止,那麼如果需要執行後續任務,將會有一個新的線程替代它。)任務保證按順序執行,並且在任何給定時間都不會有多個任務處於活動狀態。與其他等價的newFixedThreadPool(1)不同,返回的執行器保證不可重新配置以使用其他線程。

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

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

此刻我們注意到FinalizableDelegatedExecutorService包裝ThreadPoolExecutor對象,其實FinalizableDelegatedExecutorService繼承自DelegatedExecutorService,該類是隻公開ExecutorService實現的ExecutorService方法的包裝器類。

2.5 newSingleThreadScheduledExecutor

創建一個單線程執行器,該執行器可以安排命令在給定的延遲之後運行,或者定期執行。(但是請注意,如果這個線程在關閉之前的執行過程中由於失敗而終止,那麼如果需要執行後續任務,將會有一個新的線程替代它。)任務保證按順序執行,並且在任何給定時間都不會有多個任務處於活動狀態。與其他等價的newScheduledThreadPool(1)不同,返回的執行器保證不可重新配置以使用其他線程。

    public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1));
}

    public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) {
        return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1, threadFactory));
    }

提供兩種線程工廠

  • DefaultThreadFactory:默認的線程工廠
    static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;

        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                                  Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                          poolNumber.getAndIncrement() +
                         "-thread-";
        }

        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            if (t.isDaemon())
                t.setDaemon(false); // 非守護線程
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY); // 優先級
            return t;
        }
    }

默認的線程工廠通過newThread(Runnable)方法創建新的線程。並確定新線程的線程組,默認名字(格式:pool-poolNumber-thread-threadNumber);設置爲非守護線程,便於銷燬;設置新線程的優先級。

  • PrivilegedThreadFactory:享有特權的線程工廠。
    它可以捕獲訪問控制上下文acc和類加載器ccl。
    static class PrivilegedThreadFactory extends DefaultThreadFactory {
        private final AccessControlContext acc;
        private final ClassLoader ccl;

        PrivilegedThreadFactory() {
            super();
            SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                // Calls to getContextClassLoader from this class
                // never trigger a security check, but we check
                // whether our callers have this permission anyways.
                sm.checkPermission(SecurityConstants.GET_CLASSLOADER_PERMISSION);

                // Fail fast
                sm.checkPermission(new RuntimePermission("setContextClassLoader"));
            }
            this.acc = AccessController.getContext();
            this.ccl = Thread.currentThread().getContextClassLoader();
        }

        public Thread newThread(final Runnable r) {
            return super.newThread(new Runnable() {
                public void run() {
                    AccessController.doPrivileged(new PrivilegedAction<Void>() {
                        public Void run() {
                            Thread.currentThread().setContextClassLoader(ccl);
                            r.run();
                            return null;
                        }
                    }, acc);
                }
            });
        }
    }

在創建新線程的時候設置當前線程的控制上下文和類加載器與調用該線程工廠的線程的控制上下文和類加載器一致。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章