一、简介
Executors工具类提供了四种线程池的创建分别为:newSingleThreadExecutor(单线程池)、newFixedThreadPool(固定线程池)、newCachedThreadPool(缓存线程池)、newScheduledThreadPool(周期线程池),四种线程池各有特点,分别介绍下:
1.newSingleThreadExecutor:创建一个单线程的线程池,这个线程池只有一个线程在工作,线程池保证所有任务的执行顺序按照任务的提交顺序执行。
public static void main(String[] args) {
ExecutorService executor1 = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int j = i+1;
executor1.execute(() -> {
System.out.println(j+" "+Thread.currentThread().getName());
});
}
executor1.shutdown();
}
newSingleThreadExecutor方法:可见核心线程为1,最大线程数为1,用的容量为Integer.MAX_VALUE的队列(如果任务堆积,有OOM风险)。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
2.newFixedThreadPool: 创建一个固定长度线程池,可控制线程最大并发数,超出的线程会在队列中等待。
public static void main(String[] args) {
ExecutorService executor3 = Executors.newFixedThreadPool(2);
for (int i = 0; i < 10; i++) {
final int j = i+1;
executor3.execute(() -> {
System.out.println(j+" "+Thread.currentThread().getName());
});
}
executor3.shutdown();
}
newFixedThreadPool方法:核心线程数与最大线程数想等,保证最大并发量,用的容量为Integer.MAX_VALUE的队列(如果任务堆积,有OOM风险)。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
3.newCachedThreadPool:创建一个可缓存线程池,如果有空闲线程,超过存活时间回收,如果没空闲处理任务,则新建线程。
public static void main(String[] args) {
ExecutorService executor2 = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int j = i+1;
executor2.execute(() -> {
System.out.println(j+" "+Thread.currentThread().getName() +" " +System.currentTimeMillis());
});
}
executor2.shutdown();
}
newCachedThreadPool方法:核心线程数为0,最大线程数为Integer.MAX_VALUE(任务堆积,并且每个任务执行时间过长,有OOM风险),空闲线程存活时间为60S,使用容量为0的传递队列,即来一个任务就交给线程处理。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
4.newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行。
public static void main(String[] args) {
ScheduledExecutorService executor4 = Executors.newScheduledThreadPool(2);
for (int i = 0; i < 3; i++) {
final int j = i+1;
//延时j秒后执行
executor4.schedule(() ->{
System.out.println(j+" "+Thread.currentThread().getName() +" " +System.currentTimeMillis());
},j,TimeUnit.SECONDS);
//延时 + 定时 首次执行延迟2秒 之后每次与上次执行间隔两秒
executor4.scheduleAtFixedRate(() -> {
System.out.println("第 " + j +"个任务开始执行" +" "+ System.currentTimeMillis());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第 " + j +"个任务开始执行结束");
// System.out.println(j+" "+Thread.currentThread().getName() +" " +System.currentTimeMillis());
},2,2,TimeUnit.SECONDS);
}
// 延时 + 定时 把shutdown 注释掉看效果
executor4.shutdown();
}
newScheduledThreadPool方法:最终调用的ThreadPoolExecutor的构造方法,核心线程数为corePoolSize,最大线程数为Integer.MAX_VALUE(任务堆积,创建大量线程,有OOM风险),基于DelayedWorkQueue优先级队列实现延时和定时功能。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
二、基于ThreadPoolExecutor自定义线程池
通过上文可发现在任务量堆积过多的时候,上述四种线程池会有OOM风险,并且默认的拒绝策略都是AbortPolicy,实际运行的要执行的任务,不应该直接不管(当然线程池设置合理,很难遇到这种情况),为了规避这两点问题,尝试基于ThreadPoolExecutor自定义一个线程池,但核心线程池的数量该如何设置才能最好的利用CPU性能呢,《Java Concurrency in Practice》即《java并发编程实践》书中给出公式为:
threads=Ncpu * Ucpu * (1+w/c)
Ncpu=CPU核心数
Ucpu=CPU使用率,0~1
W/C=等待时间与计算时间的比率
IO密集型:一般情况下,如果存在IO,那么肯定w/c>1(阻塞耗时一般都是计算耗时的很多倍),保守点取1即,Ucpu肯定想全利用,threads=Ncpu*(1+1)=2Ncpu。
计算密集型:假设没有等待w=0,则W/C=0. threads=Ncpu。
Ncpu可通过Runtime.getRuntime().availableProcessors()获取。
目前开发多为WEB应用,包含网络传输、数据库、存间的交互都涉及到IO。所以核心线程数量可以定义为Runtime.getRuntime().availableProcessors()*2
代码如下:
package com.company.threadpool;
import java.util.concurrent.*;
public class DIYThreadPool {
//防止实例化
private DIYThreadPool(){};
//核心线程数
private static final int CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors()*2;
//最大线程数
private static final int MAXIMUM_POOL_SIZE = Runtime.getRuntime().availableProcessors()*4;
//存活时间
private static final long KEEP_ALIVE_Time = 60L;
private static final TimeUnit UNIT = TimeUnit.SECONDS;
//默认队列大小
private static final int TASK_SIZE = 500;
//阻塞队列
private static BlockingQueue<Runnable> taskQueue = new ArrayBlockingQueue<>(TASK_SIZE);
//线程池名字
private static final String THREAD_POOL_NAME = "DIYThreadPool";
//单例 静态内部类
private static class Instance {
private final static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE,MAXIMUM_POOL_SIZE,KEEP_ALIVE_Time,UNIT,taskQueue,new MyThreadFactory(),new MyRejectedExecutionHandler());
}
public static ThreadPoolExecutor getInstance() {
return Instance.threadPool;
}
/**
* 自定义线程工厂类
*/
public static class MyThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r,"");
thread.setName(THREAD_POOL_NAME + "-"+thread.getId());
//设定非守护线程
if (thread.isDaemon())
thread.setDaemon(false);
//去掉优先级
if (thread.getPriority() != Thread.NORM_PRIORITY)
thread.setPriority(Thread.NORM_PRIORITY);
return thread;
}
}
/**
* 自定义拒绝策略
*/
public static class MyRejectedExecutionHandler implements RejectedExecutionHandler{
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
//1.记录日志 存到 redis 有空闲时间取出来执行
//2.重试 直到放进去
//3.重试N次后 存日志 redis 有空闲时间取出来执行
//以上根据业务实现
System.out.println("线程池:"+executor +"拒绝执行任务...:"+r);
}
}
}