概述
創建線程本身開銷大,反覆創建並銷燬,過多的佔用內存。所以有大量線程創建考慮使用線程池。線程池不用反覆創建線程達到線程的複用,更具配置合理利用cpu和內存減少了開銷,性能會得到提高,還能統一管理任務
比如服務器收到大量請求,每個請求都分配線程去處理,對服務器性能考驗就比較大,如果創建5個以上線程考慮使用線程池。
線程複用原理
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
//獲取線程池的線程
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//如過task不等於null,或者獲取的task不爲空
while (task != null || (task = getTask()) != null) {
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
//複用執行
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
線程數量:
cpu密集型(加密,計算hash等):cpu核心數1到2倍左右
耗時IO型(讀寫數據庫,文件網絡讀寫)一般是cpu核心數多倍充分利用cpu
線程數公式:cpu核心數x(1+平均等待時間/平均工作時間)
線程池創建
線程池創建使用線程工具類創建
//線程池頂層接口
public interface Executor {
void execute(Runnable command);
一般我們用Executor實現接口創建線程池
public interface ExecutorService extends Executor{
//重要方法
/**
* 初始化關閉過程,並不會直接關閉,在任務執行完畢後關閉,
* 不在增加新任務
*/
void shutdown();
/**
* 判斷線程池是否已經關閉的狀態,關閉返回true,否則返回false
*/
boolean isShutdown();
/**
* 判斷線程是否已經完全終止,任務已經執行完畢
*/
boolean isTerminated();
/**
* 等待一定時間線程是否終止,執行完畢返回true,否則返回false
*
* @throws InterruptedException 如果期間被打斷
*/
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
}
/**
* 停止所有正在執行的任務,停止處理等待的任務,並返回等待執行的任務列
* 表。
*
* @return 返回一個集合並返回
*/
List<Runnable> shutdownNow();
}
/**
* 線程池工具類,用來創建線程池,提供四個靜態方法創建不同的線程池·
*返回 ExecutorService實現類也就是線程池對象(ThreadPoolExecutor)
*
*/
public class Executors {
}
//案例
private static ExecutorService executorService = Executors.newFixedThreadPool(1);
//提交任務,Task爲一個具體線程類
executorService.execute(new Task());
ExecutorService實現類(ThreadPoolExecutor)構造方法參數解析
public ThreadPoolExecutor(int corePoolSize, //核心線程數
//最大線程數
int maximumPoolSize,
//線程保持存活時間,如果線程數多餘核心數
//量而且限制,會回收閒置的線程
long keepAliveTime,
//時間單位
TimeUnit unit,
//任務存儲隊列
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
//ThreadFactory是當線程池需要創建新線程底層使用線程工廠
//創建,Handler是 線程無法接受新的任務的拒絕策略
Executors.defaultThreadFactory(), defaultHandler);
}
參數執行流程:
任務進來,核心線程處理,任務數多於核心數,將任務放進隊列中,隊列滿了就創建新的線程但是線程總數小於最大線程數,如果任務多餘最大線程數,就執行拒絕策略,不在接受任務
拒絕策略
- 線程池已經關閉,提交任務會失敗
- 任務隊列滿了而且線程數線程數已經到了最大線程數提交任務失敗
拒絕策略分類 - AbortPolicy()默認的策略,直接拋出異常
- DiscardPolicy()直接默默丟棄
- DiscardOldestPolicy()隊列最老的丟棄
- CallerRunsPolicy()誰提交線程誰去執行發,比較人性化,給線程池反衝
如果核心線程數等於最大線程數就會創建固定的線程數的線程池,默認線程工廠能解決大部分情況
Executors創建線程的四個靜態方法
更具業務場景選擇合適的線程池
/**
* 創建固定大小的線程數量量的線程池,核心數量等於最大數量
* 都是nThreads,存活時間爲0沒有意義,無界隊列,請求越來越多
* 就會進入隊列,容易OOM(內存溢出)
*
* @param 線程數量
* @return 返回線程池
* @throws IllegalArgumentException 參數異常
*/
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
/**
*創建一個只有一個線程的線程池,同樣容易OOM
*
* @return 返回線程池
*/
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
/**
* 核心數爲0,最大線程數爲Integer.MAX_VALUE,無論多少任務過來都會新建
* 線程去處理,60秒會回收閒置的線程,同樣容易OOM
*
* @return 返回線程池
*/
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
/**
* 該線程池可以安排命令在給定延遲後運行或定期執行。同樣容易OOM
* @param 核心數
* @return 返回線程池
* @throws 參數異常
*/
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
public static void main(String[] args) {
ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(10);
//延遲五秒執行
// threadPool.schedule(new Task(), 5, TimeUnit.SECONDS);
//延遲一秒秒執行,每隔三秒執行一次
threadPool.scheduleAtFixedRate(new Task(), 1, 3, TimeUnit.SECONDS);
}
正確創建線程池的方式
言外之意就是默認四種創建線程池的方式是不正確的,首先默認的靈活度不夠,難以適應各種業務場景,而且容易OOM,阿里巴巴編程規範明確規定不要用默認的方式創建線程池,而是直接用ThreadPoolExecutor這個實現類
最佳實踐:
ThreadPoolExecutor poolExecutor=new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler)
poolExecutor.execute(Task);
//自定義線程池
/**
* 描述: 演示每個任務執行前後放執行邏輯
*/
public class PauseableThreadPool extends ThreadPoolExecutor {
//聲明一個鎖
private final ReentrantLock lock = new ReentrantLock();
//阻塞工具
private Condition unpaused = lock.newCondition();
//標誌位
private boolean isPaused;
//一系列重寫構造方法
public PauseableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
public PauseableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
}
public PauseableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
}
public PauseableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory, RejectedExecutionHandler handler) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory,
handler);
}
//每次任務執行都會執行這個方法
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
lock.lock();
try {
while (isPaused) {
//阻塞線程池
unpaused.await();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
private void pause() {
lock.lock();
try {
isPaused = true;
} finally {
lock.unlock();
}
}
public void resume() {
lock.lock();
try {
isPaused = false;
//喚醒線程池
unpaused.signalAll();
} finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
PauseableThreadPool pauseableThreadPool = new PauseableThreadPool(10, 20, 10l,
TimeUnit.SECONDS, new LinkedBlockingQueue<>());
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("我被執行");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
for (int i = 0; i < 10000; i++) {
pauseableThreadPool.execute(runnable);
}
Thread.sleep(1500);
pauseableThreadPool.pause();
System.out.println("線程池被暫停了");
Thread.sleep(1500);
pauseableThreadPool.resume();
System.out.println("線程池被恢復了");
}
}
線程池狀態
- RUNNING :接受任務並處理排隊任務
- SHUTDOWN :不接受新任務,但處理排隊任務
- STOP:不接受新任務,也不處理任務,突然終止
- TIDYING:任務都終止,運行terminate終止線程池
- TERMINATED:terminate()線程池不在啓動