天天看點

Java并發程式設計一四種常用線程池簡述

推薦:​​Java并發程式設計彙總​​

Java并發程式設計一四種常用線程池簡述

我們使用線程是用來執行任務的,是以,首先我們得有一個任務,這裡我們定義線程的任務很簡單,就是輸出自己(目前線程)的​

​name​

​​,為了友善觀察,在每個線程的執行邏輯中​

​sleep(500)​

​。

任務

​Task​

​​類,實作了​

​Runnable​

​接口。

package threadpool;

class Task implements Runnable {

    @Override
    public void run() {
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName());
    }
}      

SingleThreadExecutor

測試代碼:

package threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SingleThreadExecutor {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 1000; i++) {
            executorService.execute(new Task());
        }
    }
}      

​ExecutorService​

​​接口繼承了​

​Executor​

​​接口,它比​

​Executor​

​​接口有更豐富的方法,便于我們使用。

​​

​Executors​

​是Java線程池的工具類。

輸出:

pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1      

上面的輸出其實隻是一部分,很顯然會輸出一千個​

​pool-1-thread-1​

​。

​SingleThreadExecutor​

​這種線程池隻會建立一個線程去執行所有的任務。

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

​corePoolSize=1​

​​,​

​maximumPoolSize=1​

​​,很明顯核心線程數和最大線程數都是​

​1​

​​,這裡的​

​keepAliveTime=0​

​​說明線程池不需要去試圖銷毀線程,因為​

​keepAliveTime​

​​對核心線程是沒有限制的,又​

​corePoolSize​

​​和​

​maximumPoolSize​

​​都是​

​1​

​,即線程池的線程都是核心線程。

​workQueue​

​​是​

​LinkedBlockingQueue​

​​,等以後寫阻塞隊列相關的部落格時,再詳細介紹​

​LinkedBlockingQueue​

​​,現在我們隻需要知道​

​LinkedBlockingQueue​

​​這種阻塞隊列,調用它的無參構造器,建立的阻塞隊列的容量是​

​Integer.MAX_VALUE​

​​,你可以認為是無窮大。這也很符合​

​SingleThreadExecutor​

​​的特性,這種線程池隻會建立一個線程來執行任務,而線程池建立​

​corePoolSize​

​​個核心線程後,需要等待任務隊列滿了之後再去建立其他的線程來幫忙,而​

​SingleThreadExecutor​

​​這種線程池的任務隊列容量無窮大(這裡認為​

​Integer.MAX_VALUE​

​為無窮大),也就無需建立核心線程以外的線程來幫忙了。

/**
     * Creates a {@code LinkedBlockingQueue} with a capacity of
     * {@link Integer#MAX_VALUE}.
     */
    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }      

FixedThreadPool

測試代碼:

package threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class FixedThreadPoolTest {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(4);
        for (int i = 0; i < 1000; i++) {
            executorService.execute(new Task());
        }
    }
}      

輸出:

pool-1-thread-2
pool-1-thread-3
pool-1-thread-4
pool-1-thread-1
pool-1-thread-4
pool-1-thread-2
pool-1-thread-1
pool-1-thread-3
pool-1-thread-3
pool-1-thread-2
pool-1-thread-1
pool-1-thread-4
pool-1-thread-2
pool-1-thread-4
pool-1-thread-3      

上面的輸出也是輸出的一部分,這種線程池可以和​

​SingleThreadExecutor​

​​進行對比了解,​

​SingleThreadExecutor​

​​建立一個線程執行任務,而​

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

和​

​SingleThreadExecutor​

​的建立方法很相似,就不多說了。

CachedThreadPool

測試代碼:

package threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CachedThreadPool {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 1000; i++) {
            executorService.execute(new Task());
        }
    }
}      

輸出:

pool-1-thread-791
pool-1-thread-868
pool-1-thread-864
pool-1-thread-790
pool-1-thread-853
pool-1-thread-789
pool-1-thread-863
pool-1-thread-717
pool-1-thread-776
pool-1-thread-865
pool-1-thread-861
pool-1-thread-782
pool-1-thread-857
pool-1-thread-859
pool-1-thread-858
pool-1-thread-751
pool-1-thread-692      

上面的輸出也是輸出的一部分,可見這種線程池建立線程就比較随意了。

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

​SynchronousQueue​

​​實際上不是一個真正的隊列,因為它不會為隊列中元素維護存儲空間,與其他隊列不同的是,它維護一組線程,這些線程在等待着把元素(任務)加入或移出隊列,是以可以知道,當任務來了,​

​SynchronousQueue​

​​就把任務交給線程池,如果線程池沒有空閑的線程,就會建立線程去處理這個任務,這就是為什麼這種線程池建立線程看起來比較随意了,​

​corePoolSize=0​

​​說明這種線程池沒有核心線程,也就是線程池可以試圖去銷毀所有閑置的線程,​

​maximumPoolSize=Integer.MAX_VALUE​

​​可以認為這種線程池可以無限地建立線程(這裡認為​

​Integer.MAX_VALUE​

​為無窮大)。

圖示如下:

Java并發程式設計一四種常用線程池簡述

這裡的​

​keepAliveTime=60L​

​說明線程池會試圖銷毀線程,當線程閑置60秒(機關是秒)後就可能會被銷毀。

ScheduledThreadPool

測試代碼:

package threadpool;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledThreadPoolTest {

    public static void main(String[] args) {
        ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(10);
        threadPool.schedule(new Task(), 5, TimeUnit.SECONDS);
    }
}      

輸出:

pool-1-thread-1      
/**
     * Creates and executes a one-shot action that becomes enabled
     * after the given delay.
     *
     * @param command the task to execute
     * @param delay the time from now to delay execution
     * @param unit the time unit of the delay parameter
     * @return a ScheduledFuture representing pending completion of
     *         the task and whose {@code get()} method will return
     *         {@code null} upon completion
     * @throws RejectedExecutionException if the task cannot be
     *         scheduled for execution
     * @throws NullPointerException if command is null
     */
    public ScheduledFuture<?> schedule(Runnable command,
                                       long delay, TimeUnit unit);      

​delay​

​​為​

​5​

​​,機關為秒,也就是延遲​

​5​

​秒執行,并且這個調用隻會執行一次任務。

測試代碼:

package threadpool;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledThreadPoolTest {

    public static void main(String[] args) {
        ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(10);
        threadPool.scheduleAtFixedRate(new Task(), 1, 1, TimeUnit.SECONDS);
    }
}      

輸出:

pool-1-thread-1
pool-1-thread-1
pool-1-thread-2
pool-1-thread-1
pool-1-thread-3
pool-1-thread-2
pool-1-thread-4
pool-1-thread-1
pool-1-thread-5
pool-1-thread-3      

輸出隻是一部分。

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

​initialDelay=1​

​​、​

​period=1​

​​,機關為秒,也就是延遲​

​1​

​​秒執行任務,并且以​

​1​

​秒為一個周期一直執行任務。

顯然這種線程池,很适合執行具有周期性的任務。

/**
     * Creates a new {@code ScheduledThreadPoolExecutor} with the
     * given core pool size.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @throws IllegalArgumentException if {@code corePoolSize < 0}
     */
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }