多線程的常用實現/常用的線程池
根據阿里對使用線程池的規範:可以參考Executors的實現,按照業務實現自己的線程池。
注意線程池等資源還是要想着關閉。
初始化時,來一個任務新建一個一個線程;直到核心線程數滿,再往隊列裏面放任務;如果隊列也滿了就繼續新建線程到最大線程數量;如果最大線程數滿就使用拒絕策略;Executor的默認拒絕策略是AbortPolicy;Spring線程池默認是同步執行。
public static void main(String[] args) {
int threadCount = 10;
// 依次設置核心線程數、最大線程數、(大於核心數部分)空閒存活時間、存活時間單位、隊列
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(threadCount, threadCount, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
//固定大小線程池、
ExecutorService executorService = Executors.newFixedThreadPool(10);
//無界線程池
Executors.newCachedThreadPool();
//單個線程池 只有一個線程池的固定大小線程池FIFO
Executors.newSingleThreadExecutor();
/** ---------------- 其他線程池--------------**/
//用於任務調度的線程池
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
//工作竊取線程池
ExecutorService executorServiceFk = Executors.newWorkStealingPool();
}
拒絕策略
- CallerRunsPolicy 同步執行(調用當前線程池的所在的線程去執行被拒絕的任務)
- AbortPolicy 拋出個RejectedExecutionException異常,不執行此任務
- DiscardPolicy 直接拋棄任務
- DiscardOldestPolicy 會拋棄最舊的任務
- 自定義拒絕策略
線程池常用方法
execute(Runnable) //無返回值
submit(Runnable/Callable) // 有返回值
shutdown:平滑的關閉ExecutorService,不可以提交新的task,已經提交的將繼續執行(包含提交正在執行和提交未執行)
當所有已提交任務執行完畢,線程池即被關閉。
提交使用拒絕策略處理。
awaitTermination:當等待超過設定時間時,會監測ExecutorService是否已經關閉,
若關閉則返回true,否則返回false。一般情況下會和shutdown方法組合使用
接收timeout和unit兩個參數,用於設定超時時間及單位。
shutdownNow :
(1)線程池的狀態立刻變成STOP狀態,此時不能再往線程池中添加新的任務。
(2)終止等待執行的線程,並返回它們的列表;
(3)試圖停止所有正在執行的線程,試圖終止的方法是調用Thread.interrupt(),但是大家知道,如果線程中沒有sleep 、wait、Condition、定時鎖等應用, interrupt()方法是無法中斷當前的線程的。所以,ShutdownNow()並不代表線程池就一定立即就能退出,它可能必須要等待所有正在執行的任務都執行完成了才能退出。
線程池數量設置
cpu密集型:核心線程數 = cpu(處理器)數量
Io密集型:核心線程數 = cpu(處理器)數量 * 2
其他線程池
線程池主要分兩類,ThreadPoolExecutor和ForkJoinPool;使用較多的是ThreadPoolExecutor,也可以通過Executors來創建需要的線程池;
線程池解決了兩個問題: 一是在執行大量的異步任務時,因爲線程池減少了任務開始前的準備工作,如頻繁創建線程,啓動線程等工作,提升了性能表現;二是提供了一種綁定資源和管理資源的途徑,可以進行一些基礎的統計分析,比如已經完成的任務數量等。
任務調度線程池
ScheduledExecutorService使用示例
@Slf4j
public class ScheduledThreadPoolDemo implements Runnable{
public static void main(String[] args) throws ExecutionException, InterruptedException {
//兩種創建方式,都是一個代碼
//ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(10);
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
//使用ScheduledThreadPool來延遲執行Callable或者Runnable,返回一個對象可用來取消或者查詢任務執行情況
//ScheduledFuture future = scheduledExecutorService.schedule(Callable<V> callable, long delay, TimeUnit unit);
// Callable c1 = new CallableDemo("A"); RunnableDemo r1 = new RunnableDemo();
// ScheduledFuture future = scheduledExecutorService.schedule(c1, 3, TimeUnit.SECONDS);
// future.isDone();
//循環執行任務,在等待initialDelay.unit後執行,然後每隔period.unit後實行;同樣返回ScheduledFuture對象;
//如果此任務的任何一個執行要花費比其週期更長的時間,則將推遲(一定時間)後續執行,但不會同時執行。週期爲4秒,執行耗時3秒,每次開始打印間隔一秒;
//如果任務的任何一個執行遇到異常,則後續執行都會被取消。否則,只能通過執行程序的取消或終止方法來終止該任務。
//scheduledExecutorService.scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);
ScheduledThreadPoolDemo scheduledThreadPoolDemo = new ScheduledThreadPoolDemo();
//scheduledExecutorService.scheduleAtFixedRate(scheduledThreadPoolDemo, 5, 4, TimeUnit.SECONDS);
//和上一個方法差不多,但是會等上一個任務結束,然後再等待delay時間去執行
//scheduledExecutorService.scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
scheduledExecutorService.scheduleWithFixedDelay(scheduledThreadPoolDemo, 5, 4, TimeUnit.SECONDS);
}
@Override
public void run() {
log.info("----執行方法開始---");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("----執行方法結束---");
}
}
工作竊取線程池 ForkJoinPool
ForkJoin併發框架:Fork=分解 + Join=合併
ForkJoinPool:ForkJoin線程池,實現了ExecutorService接口和工作竊取算法,用於線程調度與管理。
ForkJoinTask:ForkJoin任務,提供了fork()方法和join()方法。通常不直接使用,而是使用以下子類: RecursiveAction:無返回值的任務,通常用於只fork不join的情形。RecursiveTask:有返回值的任務,通常用於fork+join的情形。
根據ForkJoinTask的兩種類型,可以將ForkJoin併發框架劃分爲兩種用法:
only fork:遞歸劃分子任務,分別執行,但是並不需要合併各自的執行結果。
fork+join:遞歸劃分子任務,分別執行,然後遞歸合併計算結果。
參考轉載文章:https://blog.csdn.net/hanchao5272/article/details/79982095
public class TestForkJoinPool {
public static void main(String[] args) {
MyTask mt = new MyTask(1);
// Executors和new ForkJoinPool實現是一樣的,默認情況下Runtime.getRuntime().availableProcessors()得到線程數
//ExecutorService executorServiceFk = Executors.newWorkStealingPool();
ForkJoinPool forkJoinPool = new ForkJoinPool(10);
Future<Integer> result = forkJoinPool.submit(mt);
try {
System.out.println(result.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
forkJoinPool.shutdown();
}
public static class MyTask extends RecursiveTask<Integer> {
int i;
public MyTask(int i) {
this.i = i;
}
@Override
protected Integer compute() {
if (i >= 100) {
return i * i;
}
MyTask newTask2 = new MyTask(i + 1);
newTask2.fork();
return i*i + newTask2.join();
}
}
}