线程池篇
建议使用ThreadPoolExecutor类进行线程池的创建,更加细粒度的管理自己所使用的线程池,对线程池的分配也根据自己的实际情况来具体的控制
如果使用线程池可能会导致OOM(outofMemoryError)
核数的获取
获取当前电脑的核数:Runtime.getRuntime().availableProcessors();
ThreadPoolExecutor
线程池最底层的类ThreadPoolExecutor
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
2,
/*
* IO 密集型 CPU*2
* CPU密集型 CPU核数+1 (逻辑处理器+1)
* */
5,
60L,
TimeUnit.SECONDS,
//默认是Integer.Max_Value
new LinkedBlockingQueue<Runnable>(3),
Executors.defaultThreadFactory(),
/**
* abort RejectedExecutionException
* CallerRunsPolicy 会回退到上一个线程,比如main线程
* DiscardPolicy 放心新来的
* DiscardOldestPolicy 放弃阻塞队列中最开始的那个
*/
new ThreadPoolExecutor.DiscardOldestPolicy());
Executors工具类
newWorkStealingPool(1.8)
public static ExecutorService newWorkStealingPool(int parallelism) {
return new ForkJoinPool
(parallelism,
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
public ForkJoinPool(int parallelism,
ForkJoinWorkerThreadFactory factory,
UncaughtExceptionHandler handler,
boolean asyncMode) {
this(checkParallelism(parallelism),
checkFactory(factory),
handler,
asyncMode ? FIFO_QUEUE : LIFO_QUEUE,
"ForkJoinPool-" + nextPoolId() + "-worker-");
checkPermission();
}
private ForkJoinPool(int parallelism,
ForkJoinWorkerThreadFactory factory,
UncaughtExceptionHandler handler,
int mode,
String workerNamePrefix) {
this.workerNamePrefix = workerNamePrefix;
this.factory = factory;
this.ueh = handler;
this.config = (parallelism & SMASK) | mode;
long np = (long)(-parallelism); // offset ctl counts
this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
}
//分支合并线程池
newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- 创建固定的线程数,创建的初始值和最大值都是一个
- 创建的阻塞队列无上限
newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
- 带缓存的线程池数,当开始的时候是0,最大是Integer.MAX_VALUE
- 最长六十秒不被使用则被回收
newSingleThreadPool
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}
- 创建一个单个的线程池(里面只有一个)
newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
- 创建一个带时间的线程池,使用的是DelayedWorkQueue拒绝阻塞队列
线程池的使用
- execute():void
- submit():Future
对无返回值的线程执行
try {
for (int i = 0; i < 9; i++) {
poolExecutor.execute(() -> {
System.out.println(Thread.currentThread().getName() + " ");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
poolExecutor.shutdown();
}
对有返回值的线程执行
poolExecutor.submit(new FutureTask<>(() -> 1024)).get();
线程池的执行流程
- 首先来的线程任务开始开启小于等于corePoolSize的线程进行执行
- 如果有些线程还是没执行完,但是有新的任务来了,那么将会进入阻塞队列
- 如果阻塞队列满了,就会去开启新的线程来执行任务,线程<=MaxiNumPoolSize
- 当大于corePoolSize的线程在KeepAliveTime内没被使用则会被释放
- 如果开启了最大的线程还是有任务没有被执行,则会进行使用拒绝策略进行对新任务的拒绝
线程池具体参数的配置*
线程池的拒绝策略
当等待队列满了&线程池的最大线程数也达到了,当出现新的任务时,会执行拒绝策略
new ThreadPoolExecutor.CallerRunsPolicy()
- abortPolicy 抛出RejectedExecutionException,阻止系统正常运行
- CallerRunsPolicy 会回退到上一个线程(调用它的线程),比如main线程
- DiscardPolicy 放弃新来的task,如果允许任务丢失,则是最好的一种策略
- DiscardOldestPolicy 放弃阻塞队列中最开始的那个
上述策略皆实现了RejectedExecutionHandler
接口
线程池最大线程数的设定
CPU密集型
- CPU核数+1个线程的线程池
IO密集型
- IO密集型任务线程并不是一直在执行任务,则应尽可能多的线程,CUP核数*2
- CUP核数 / 1-阻塞系数 (阻塞系数在0.8~0.9之间)
- 比如8核CPU: 4/1-0.9 = 40