java中的線程池

Java中的線程池是運用場景最多的併發框架,幾乎所有需要異步或者併發執行任務的程序都可以使用線程池。
具體的使用場景:
舉例分析

使用線程池的好處

降低資源消耗 創建和銷燬線程需要消耗資源,線程池可以重複利用已創建的線程,減少資源消耗。

提高響應速度 創建線程需要時間,當任務到達時,立即執行,無需等待線程創建,這樣可以節省時間,提高響應速度。

提高線程的可管理性 線程的創建、銷燬、控制管理、調優等都交由線程池統一管理和分配。

線程池的實現原理

線程池的處理流程

核心線程池(corePool)
工作隊列(BlockingQueue)
線程池(maximumPool)

向線程池提交一個任務後
1、線程池先判斷corePool中的線程是否都在執行任務,即判斷當前運行的線程數與corePoolSize的大小,如果小於,則創建新的線程來執行任務(注意,執行這一步要獲取全局鎖)。否則進入下一步。

2、線程池判斷工作隊列是否已經滿,如果沒有滿,則將新提交的任務存儲在這個工作隊列裏。否則進入下一步。

3、線程池判斷線程池的線程是否都處於工作狀態,即判斷即判斷當前運行的線程數與maximumPoolSize的大小,如果小於,則創建新的線程來執行任務(注意,執行這一步要獲取全局鎖)。否則,則交給飽和策略來處理,即任務被拒絕,調用RejectedExecutionHandler.rejectedExecution()方法。

線程池的使用

線程池的創建

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), defaultHandler);
    }

參數分析:

corePoolSize 線程池的基本大小
當提交一個任務到線程池時,線程池就會創建一個線程來執行任務。這裏要注意的是,只要corePoolSize沒有達到最大值,即使線程池中存在空閒線程,線程池還是會創建新線程來執行任務。等到需要執行的任務數大於線程池的基本大小時就不再創建。

workQueue 工作隊列
用於保存等待執行的任務的阻塞隊列。可以選擇的阻塞隊列有:
ArrayBlockingQueue
LinkedBlockingQueue
SynchronousQueue
PriorityBlockingQueue

maximumPoolSize 線程池最大數量
如果隊列滿了,但已創建的線程數小於最大線程數,則線程池會創建新的線程來執行任務。但如果工作隊列使用了無界的隊列,則這個參數就沒用了。

defaultHandler

 private static final RejectedExecutionHandler defaultHandler =
        new AbortPolicy();

代碼中可以看出飽和策略默認是AbortPolicy,表示無法處理新任務時拋出異常

向線程池提交任務

execute()
用於提交不需要返回值的任務。因爲沒有返回值,所以無法判斷任務是否被線程池執行成功。
源碼

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

submit() 用於提交需要返回值的任務。

public class ThreadPoolExecutor extends AbstractExecutorService {
}
public abstract class AbstractExecutorService implements ExecutorService {
    ...
    ...
    ...
    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }
    ...
    ...
    ...
}
Future<Object> future = executor.submit(returnValueTask);
try{
    Object s = future.get();

}catch(InterruptedException e){

}catch(ExecutionException e){

}finally{

}

線程池返回一個Future類型對象,通過這個Future對象可以判斷任務是否執行成功,並且可以通過future.get()方法來回去返回的值。get方法會阻塞當前線程,直到任務完成,而使用get(long timeout,TimeUnit unit)方法則會阻塞當前線程一段時間後立即返回,這時候可能任務還沒有完成。

關閉線程池

線程池的監控

發佈了82 篇原創文章 · 獲贊 27 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章