Java中線程池的實現

Java中線程池的實現  

當我們要使用線程時,就要去創建線程,這樣看上去似乎很正常,但是頻繁地創建線程、銷燬線程會對系統資源造成不小地負擔,大大降低了系統地效率。

Java中有一個很好地工具,可以事先創建好一些線程,當我們需要使用地時候就直接拿來使用就可以,這樣就不會頻繁地創建和銷燬線程了,這個神奇地工具就是線程池。

線程池是通過Executor執行器來創建。


我們來看一下在java.util.concurrent包下面的Executor類都有哪些東西。

Executor提供的幾種創建線程池的方法:

1.newFixedThreadPool

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

簡單翻譯一下就是說這個類創建了些個固定數目的共享線程,即實現規定好創建的線程的數量,後面不能更改數量。

在任何一個時間點,活躍工作的線程數量都不能夠超過這個最大的數量。

當所有的線程都在工作時,如果這時候有一個新的線程需要被創建,那麼這個需要創建的線程需要就會被放進一個等待隊列BlockingQueue,直到有空餘的線程可以使用才執行。

如果有一個線程在執行期間由於發生了錯誤而終止運行,一個新的線程會接替這個終止的線程完成就下來的任務。

線程池中的線程們會一直存在直到被要求關閉。

初始化時指定nThreads線程池線程數量上限。


2.newWorkStealingPool:

/**
     * Creates a thread pool that maintains enough threads to support
     * the given parallelism level, and may use multiple queues to
     * reduce contention. The parallelism level corresponds to the
     * maximum number of threads actively engaged in, or available to
     * engage in, task processing. The actual number of threads may
     * grow and shrink dynamically. A work-stealing pool makes no
     * guarantees about the order in which submitted tasks are
     * executed.
     *
     * @param parallelism the targeted parallelism level
     * @return the newly created thread pool
     * @throws IllegalArgumentException if {@code parallelism <= 0}
     * @since 1.8
     */
    public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool
            (parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

parallelism爲並行度,newWorkStealingPool也有一個不帶參數的構造,默認並行度爲cup的個數。

這個參數規定了同一時間同時執行任務的線程數量上限。平時線程的數量是會動態增加或減少的,並不像newFixedThreadPool那樣是固定的線程數量。

同時也不保證按照任務提交的順序來完成任務,它爲了減少競爭,採取了多種的queue。


3.newCachedThreadPool

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

創建一個可以動態增長的線程池,當我們有新的任務提交時,但是當前線程池並沒有空餘的線程可以供我們使用,這時候就會新建一個線程,如果有以前創建的線程空閒下來,則不會創建新的線程而是使用以前已經創建的。

當一個線程空閒下來超過60秒,這個線程就會被從緩存線程池中移除。

這種緩存線程池在應對那些執行時間很短的異步任務時表現很好。


4.newScheduledThreadPool

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

定時週期性地執行任務,線程數量固定。

public class Main {
    public static void main(String[] args) throws Exception {
        // 指定大小爲4  
        ScheduledExecutorService m = Executors.newScheduledThreadPool(4);
        m.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                Date now = new Date();
                System.out.println("線程" + Thread.currentThread() + "報時:" + now);
            }

        }, 1, 1, TimeUnit.SECONDS); // 延遲1s秒執行,每隔1s執行一次
    }
}  


5.newSingleThreadPool:

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

顧名思義,這個線程池裏只有一個線程,任何一個時刻,最多隻有一個線程在工作。

每個被提交地任務按照先提交先執行的原則依次執行。

當原來的線程出現問題被終止了,會有一個新的線程被創建去執行後面的任務。

這個線程的阻塞隊列採用了無邊界的LinkedBlockingQueue。


還有一些擴展的比如

newSingleThreadScheduledExecutor

就是在上面一種的情況下加入了計時週期性執行。

這些就是Executor類提供給我們的創建線程池的方法,我們可以根據業務需要選擇合適的方法。



這些線程池爲了效率和應對特殊的情況,採用了不同的BlockingQueue,下面一篇文章我會介紹一下各種

BlockingQueue的實現類。

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