Java線程池入門必備


線程池

一. 線程池的簡介

1.什么是線程池?

  最早期的工作線程處理任務的模型。一個任務的到來,會伴隨着線程的創建,當處理完任務后,線程會被銷毀,資源回收。這種一個任務一個線程一系列創建銷毀的模式,缺陷毋庸置疑.不僅是線程創建銷毀帶來的系統開銷,也不好管理工作線程。於是引入了“線程池”的概念。它是一種預創建線程的技術。每次線程執行完任務前,先把任務委派給線程池空閑的線程, 如果沒有空閑的線程, 則根據線程池任務策略執行。處理完任務后, 線程不會直接被銷毀掉,會放到線程池管理。

2.線程池有何作用?

  線程池的作用, 個人理解主要有三點。

  • 減少系統資源的開銷 :避免新線程的創建、銷毀等繁瑣過程。

  • 提供系統的性能 : 池至少有一個以上的線程, 多線程協同工作, 可響應多個客戶端請求。而且可以重復利用池里空閑的線程,免去了新線程不斷地創建、銷毀過程.

  • 提高系統穩定性 :一個請求一個線程處理, 高並發請求下, 系統不得不創建大量線程來接活。大量的線程創建、銷毀會占用系統大量資源, 最終耗光系統資源, 導致系統宕機。引入線程池后,能根據系統的承載能力, 調整線程池中工作線線程的數目,防止因為消耗過多的內存,而把服務器累趴下(每個線程需要大約1MB內存,線程開的越多,消耗的內存也就越大,最后死機)。

二. 線程池的創建

線程池的創建入口Executors.真正干活的是ExecutorService

  1. newFixedThreadPool
  • 說明

  創建固定大小的線程池。每次提交一個任務,就會啟一個線程來接客,直到線程池的線程數量達到線程池的上限。

  • demo
public class PoolDemo {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);

        for(int i = 0; i<5; i++) {

            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }

        executorService.shutdown();
    }
}
result :
pool-1-thread-1
pool-1-thread-2
pool-1-thread-1
pool-1-thread-2
pool-1-thread-1
  1. newCachedThreadPool
  • 說明

   創建一個可緩存的線程池。每次提交一個任務,委派給線程池空閑的線程處理, 如果木有空閑的線程, 則直接創建新線程,任務被執行完后,當前線程加入到線程池維護。其生命周期超過一定時間會被銷毀回收。

  • demo

public class PoolDemo {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();

        for(int i = 0; i<5; i++) {

            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }

        executorService.shutdown();
    }
}
result:
pool-1-thread-1
pool-1-thread-2
pool-1-thread-4
pool-1-thread-3
pool-1-thread-5
  1. newSingleThreadExecutor
  • 說明

  創建只有一個線程的線程池。問題來了, 一個線程的線程池和普通創建一個線程一樣么?當然不一樣.線程銷毀問題。

  • demo
public class PoolDemo {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        for(int i = 0; i<5; i++) {

            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }

        executorService.shutdown();
    }
}

result:
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
  1. newScheduledThreadPool
  • 說明

   創建一個大小不受限的線程池。提供定時、周期地執行任務能力。

  • demo
public class PoolDemo {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newScheduledThreadPool(2);

        for(int i = 0; i<5; i++) {

            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName());
                }
            });
        }

        executorService.shutdown();
    }
}

result:
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-2
pool-1-thread-1
  • 定時周期執行demo2
public class PoolDemo {

    public static void main(String[] args) {
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2);

        long initialDelay = 1, delay = 1;
        
        // 應用啟動1S后,每隔1S執行一次
        
        executorService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        }, initialDelay, delay, TimeUnit.SECONDS);

        // 應用啟動1S后,每隔2S執行一次
        executorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
            }
        }, initialDelay, delay, TimeUnit.SECONDS);
    }
}

拓展scheduleWithFixedDelay | scheduleAtFixedRate 區別
看下源碼注釋

  • scheduleAtFixedRate
    /**
     * Creates and executes a periodic action that becomes enabled first
     * after the given initial delay, and subsequently with the given
     * period; that is executions will commence after
     * {@code initialDelay} then {@code initialDelay+period}, then
     * {@code initialDelay + 2 * period}, and so on.
     * If any execution of the task
     * encounters an exception, subsequent executions are suppressed.
     * Otherwise, the task will only terminate via cancellation or
     * termination of the executor.  If any execution of this task
     * takes longer than its period, then subsequent executions
     * may start late, but will not concurrently execute.
     *
     * @param command the task to execute
     * @param initialDelay the time to delay first execution
     * @param period the period between successive executions
     * @param unit the time unit of the initialDelay and period parameters
     * @return a ScheduledFuture representing pending completion of
     *         the task, and whose {@code get()} method will throw an
     *         exception upon cancellation
     * @throws RejectedExecutionException if the task cannot be
     *         scheduled for execution
     * @throws NullPointerException if command is null
     * @throws IllegalArgumentException if period less than or equal to zero
     */
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit);
  • scheduleWithFixedDelay
/**
     * Creates and executes a periodic action that becomes enabled first
     * after the given initial delay, and subsequently with the
     * given delay between the termination of one execution and the
     * commencement of the next.  If any execution of the task
     * encounters an exception, subsequent executions are suppressed.
     * Otherwise, the task will only terminate via cancellation or
     * termination of the executor.
     *
     * @param command the task to execute
     * @param initialDelay the time to delay first execution
     * @param delay the delay between the termination of one
     * execution and the commencement of the next
     * @param unit the time unit of the initialDelay and delay parameters
     * @return a ScheduledFuture representing pending completion of
     *         the task, and whose {@code get()} method will throw an
     *         exception upon cancellation
     * @throws RejectedExecutionException if the task cannot be
     *         scheduled for execution
     * @throws NullPointerException if command is null
     * @throws IllegalArgumentException if delay less than or equal to zero
     */
    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long delay,
                                                     TimeUnit unit);

不難得出如下結論:

ScheduledExecutorService 中兩種最常用的調度方法 ScheduleAtFixedRate 和 
ScheduleWithFixedDelay。ScheduleAtFixedRate 
每次執行時間為上一次任務開始起向后推一個時間間隔,即每次執行時間為 
:initialDelay, initialDelay+period, initialDelay+2*period, 
…;ScheduleWithFixedDelay 
每次執行時間為上一次任務結束起向后推一個時間間隔,即每次執行時間為:initialDela
y, initialDelay+executeTime+delay, 
initialDelay+2*executeTime+2*delay。由此可見,ScheduleAtFixedRate 
是基於固定時間間隔進行任務調度,ScheduleWithFixedDelay 
取決於每次任務執行的時間長短,是基於不固定時間間隔進行任務調度。

三. 線程池的拒絕策略

  線程池的拒絕策略是干嘛來的?它是在應接不暇的時候, 對新任務采取的執行策略(執行?丟棄and so on). RejectedExecutionHandler是拒絕任務策略的基礎接口, Jdk提供了四種拒絕策略。

1.CallerRunsPolicy

  這種策略是說線程池在沒被關閉前, 直接會去執行此任務, 否則丟棄任務。

    /**
     * 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();
            }
        }
    }

2.AbortPolicy

  AbortPolicy線程拒絕策略,簡單粗暴, 直接throw exception出來了, 丟棄任務

    /**
     * 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());
        }
    }

3.DiscardPolicy

  DiscardPolicy策略跟AbortPolicy一樣, 直接丟棄任務, 只不過人家不拋出exception罷了。

    /**
     * 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) {
        }
    }

4.DiscardOldestPolicy

  DiscardOldestPolicy策略, 是在線程池沒被關閉的情況下, 丟棄任務等待隊列中最早的任務。然后重新嘗試運行該任務。

    /**
     * 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);
            }
        }
    }

寫在最后

新博客請移步


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM