1.ForkJoinPool
分解汇总任务,用很少的线程执行很多的任务
继承RecursiveTask
类创建有返回值的task,继承RecursiveAction
创建无返回值的task,重写compute
方法m在compute
方法中来对任务进行分片,调用Task
的fork
方法fork出新的task,将任务分片执行,如下代码,任务被分城8片
public class ForkJoinPoolTests {
class MyTask extends RecursiveAction {
int i = 0;
public MyTask(int i) {
this.i = i;
}
/**
* 第一次 i -- 10
* i -- 50 task 2
* task1 -- i --> 25 task1.1 task1.2 i--> 17 task1.1.1 task1.1.2 task1.2.1 task1.2.2
* task2 -- i --> 25 task2.1 task2.2 i--> 17 task2.1.1 task2.1.2 task2.2.1 task2.2.2
* 最终 task1.1.1 task1.1.2 task1.2.1 task1.2.2 task2.1.1 task2.1.2 task2.2.1 task2.2.2
* 八个task拿到任务
*/
@Override
protected void compute() {
if (i < 20){
System.out.println("我能执行了");
}else {
i = i / 2;
new MyTask(i).fork().join();
new MyTask(i).fork().join();
}
}
}
@Test
void doTests() throws IOException {
ForkJoinPool forkJoinPool = ForkJoinPool.commonPool();
forkJoinPool.execute(new MyTask(100));
System.in.read();
}
}
最常见的ForkJoinPool应用
- 线程池的
WorkStealingPool
- stream流式计算中的
parallelStream
并行流
2.ThreadPoolExecutor
2.1.七个核心参数
corePoolSize
核心线程数maximumPoolSize
最大线程数(核心线程+非核心线程)keepAliveTime
线程存活时间(线程空闲的时间)unit
线程存活时间单位workQueue
任务队列threadFactory
线程工场类,管理线程创建的工场类,默认使用非守护线程,优先级的修改,线程名字等handler
拒绝策略(jdk默认提供了四种,可以自定义)
线程进来如果没有空闲的核心线程,并且没有达到设置的最大核心线程数的时候,会创建新的线程.如果达到核心线程的最大数量,会放到任务队列,当任务队列满的时候会创建非核心线程,非核心线程到达最大数量会执行拒绝策略.
2.2.jdk提供的拒绝策略
需要自定义符合业务场景的拒绝策略
Abort
抛出异常Discard
丢弃任务,并且不抛出异常CallerRuns
调用者线程处理任务DiscardOldest
丢掉队列中等待时间最长的任务,将新的任务加入队列
3.Executors提供的线程池
-
Executors.newSingleThreadExecutor()
核心线程数和最大线程数都是1个,单线程的线程池,队列为
LinkedBlockingQueue
,存活时间为0,任务结束就创建新的线程 -
Executors.newCachedThreadPool()
核心线程数为0,非核心线程数为
Integer.MaxValue
,线程有60秒存活期,如果没有空闲线程,每来一个新任务会创建一个新的非核心线程,任务队列使用SynchronousQueue
,如果没有线程处理提交任务的线程会阻塞任务量大小不定的情况可以使用
cachedThreadPool
-
Executors.newWorkStealingPool()
每个线程有自己单独的队列,在当前线程队列任务执行完毕,会去取其他线程队列的任务来执行.内部使用
ForkJoinPool
-
Executors.newFixedThreadPool(n)
核心线程数和最大线程数都是n,线程存活时间是0,即任务到来会创建新的线程,如果超过n个任务,会进入任务队列,使用
LinkedBlockingQueue
任务数量波动不大使用
FixedThreadPool
-
Executors.newScheduledThreadPool(n)
定时任务线程池,n为核心线程数,最大线程数为
Integer.MaxValue
,存活时间为10毫秒,任务队列为DelayedWorkQueue
,按任务时间进行执行
线程池数量大小预估,预估后对程序进行压力测试修改最合适的值
Nthread = Ncpu * Ucpu*(1 + W/C)
Ncpu 处理器的核数,
Runtime.getRuntime().availableProcessors()
获取线程数Ucpu 期望的CPU利用率
W/C 等待时间与计算时间比例