上一篇文章已經介紹了線程的基本概念以及線程相關的API,下面來看一下線程池
一、線程池框架
1、線程池的優點
- 重用線程池中的線程,避免因為線程的創建和銷毀所帶來的性能開銷。?
- 能有效控制線程池的最大并發數,避免大量線程之間因互相搶奪系統資源而導致的阻塞現象。?
- 能夠對線程進行簡單的管理,并提供定時執行以及指向間隔循環執行等功能。
2、JAVA中相關類
Executor是一個頂層接口,它里面只聲明了一個方法execute(Runnable)
ExecutorService接口繼承了Executor接口,并且聲明了一些方法:submit、invokeAll、invokeAny以及shutDown等AbstractExecutorService實現了ExecutorService接口,基本實現了ExecutorService中聲明的所有方法
ThreadPoolExecutor接口繼承了AbstractExecutorService這個類,并且實現了這些重要方法:execute()?、shutdown()、shutdownNow()
Executors提供了大量的static方法,來創建各種特性的線程池(基于ThreadPoolExecutor的構造方法)、大量的callable方法和創建ThreadFactory的方法
ThreadPoolExecutor的重要屬性
private final BlockingQueue<Runnable> workQueue;//任務緩存隊列,用來存放等待執行的任務private final ReentrantLock mainLock = new ReentrantLock();//線程池的主要狀態鎖,對線程池狀態(比如線程池大小、runState等)的改變都要使用這個鎖private final HashSet<Worker> workers = new HashSet<Worker>();//用來存放工作集private final Condition termination = mainLock.newCondition();//private int largestPoolSize;//用來記錄線程池中曾經出現過的最大線程數private long completedTaskCount;//用來記錄已經執行完畢的任務個數private volatile ThreadFactory threadFactory;//線程工廠,用來創建線程private volatile RejectedExecutionHandler handler;//任務拒絕策略private volatile long keepAliveTime;//線程存活時間 private volatile boolean allowCoreThreadTimeOut;//是否允許為核心線程設置存活時間private volatile int corePoolSize;//核心池的大小(即線程池中的線程數目大于這個參數時,提交的任務會被放進任務緩存隊列)private volatile int maximumPoolSize;//線程池最大能容忍的線程數private static final RejectedExecutionHandler defaultHandler =new AbortPolicy();//默認的任務拒絕策略private static final RuntimePermission shutdownPerm =new RuntimePermission("modifyThread");
這里要重點解釋一下corePoolSize、maximumPoolSize、largestPoolSize三個變量。
corePoolSize在很多地方被翻譯成核心池大小,其實我的理解這個就是線程池的大小。舉個簡單的例子:
假如有一個工廠,工廠里面有10個工人,每個工人同時只能做一件任務。
因此只要當10個工人中有工人是空閑的,來了任務就分配給空閑的工人做;
當10個工人都有任務在做時,如果還來了任務,就把任務進行排隊等待;
如果說新任務數目增長的速度遠遠大于工人做任務的速度,那么此時工廠主管可能會想補救措施,比如重新招4個臨時工人進來;
然后就將任務也分配給這4個臨時工人做;
如果說著14個工人做任務的速度還是不夠,此時工廠主管可能就要考慮不再接收新的任務或者拋棄前面的一些任務了。
當這14個工人當中有人空閑時,而新任務增長的速度又比較緩慢,工廠主管可能就考慮辭掉4個臨時工了,只保持原來的10個工人,畢竟請額外的工人是要花錢的。
這個例子中的corePoolSize就是10,而maximumPoolSize就是14(10+4)。
也就是說corePoolSize就是線程池大小,maximumPoolSize在我看來是線程池的一種補救措施,即任務量突然過大時的一種補救措施。
不過為了方便理解,在本文后面還是將corePoolSize翻譯成核心池大小。
largestPoolSize只是一個用來起記錄作用的變量,用來記錄線程池中曾經有過的最大線程數目,跟線程池的容量沒有任何關系。
重要方法
execute
public void execute(Runnable command) {if (command == null)throw new NullPointerException();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);}
3、線程池的分類
newFixedThreadPool
newWorkStealingPool
newSingleThreadExecutor
newCachedThreadPool
newSingleThreadScheduledExecutor
newScheduledThreadPool
重點是ThreadPoolExecutor的代碼、還需要理解線程池狀態、任務的執行、線程池中的線程初始化、任務緩存隊列及排隊策略、任務拒絕策略、線程池的關閉、線程池容量的動態調整
簡單使用Demo
package com.demo;import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPool { private int threadCount = 10;private int threadpoolCount = 3;public static void main(String[] args) {new ThreadPool().threadPoolControl();}public void threadPoolControl() { ThreadObject[] et = new ThreadObject[threadCount]; ExecutorService service = Executors.newFixedThreadPool(threadpoolCount);Collection<ThreadObject> c = new ArrayList<ThreadObject>();for (int i = 0; i < threadCount; i++) { et[i] = new ThreadObject(); c.add(et[i]);}try {service.invokeAll(c);service.shutdown();} catch (InterruptedException e) {e.printStackTrace();}}class ThreadObject implements Callable<Object>{ public Object call() throws Exception {System.out.println("當前線程:"+Thread.currentThread().getName()+",線程對象:"+this);return null;}}
}