Java线程池的创建和使用
首先,查看当前cpu核的数量的代码
System.out.println(Runtime.getRuntime().availableProcessors());
创建线程池首先想到的是使用工具类Executors中的三中方式
第一种
//创建一个单例的线程池,池中只有一个线程
ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
JDK底层的源码为
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
第二种
//创建一个固定大小的线程池
ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(5);
JDK底层的源码为
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
第三种
//创建一个可缓存的线程池,即池中的线程数量会自动增长
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
JDK底层的源码为
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
可以看到,这三种方式底层都是用 new ThreadPoolExecutor()方法,只是其中的参数有所区别而已。
但是
在阿里开发手册中明确说明不要直接使用Executors工具类直接创建线程。
七个参数
在ThreadPoolExecutor种有七个参数用来控制线程池。
//创建一个线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
corePoolSize, //核心线程数
maxPoolSize,//最大线程数
keepAliveTime,线程空闲时间
allowCoreThreadTimeout,//允许核心线程超时设置,分钟,秒,毫秒等
queueCapacity,//任务队列容量(阻塞队列)
threadFactory, //线程创建工厂
rejectedExecutionHandler,//线程的拒绝策略
);
七个参数说明:
- corePoolSize:核心线程数
* 核心线程会一直存活,及时没有任务需要执行
* 当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理
* 设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭 - maxPoolSize:最大线程数
* 当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务
* 当线程数=maxPoolSize,且任务队列已满时,线程池会采用拒绝策略(最后一个参数) - keepAliveTime:线程空闲时间
* 当阻塞队列满了或者空了达到阻塞时,等待该时间,超过该时间,自动退出。
* 当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize
* 如果allowCoreThreadTimeout=true,则会直到线程数量=0 - allowCoreThreadTimeout:允许核心线程超时
* 使用TimeUnit类设置 - queueCapacity:任务队列容量(阻塞队列)
* 当核心线程数达到最大时,新任务会放在队列中排队等待执行 - threadFactory, //线程创建工厂
* 默认一般不改变 - rejectedExecutionHandler:任务拒绝策略处理器
两种情况会拒绝处理任务:
1、当线程数已经达到maxPoolSize,切队列已满,会拒绝新任务
2、 当线程池被调用shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务
4种拒绝策略
* ThreadPoolExecutor类有几个内部实现类来处理这类情况:
* AbortPolicy : 丢弃任务,抛运行时异常 , 默认
* CallerRunsPolicy : 返回原调用者执行任务
* DiscardPolicy : 队列满了,抛弃任务,不抛出异常
* DiscardOldestPolicy :队列满了,尝试去和最早的任务竞争,也不会抛出异常!
* 实现RejectedExecutionHandler接口,可自定义处理器 - 例如:使用ThreadPoolExecutor创建线程池,使用threadPoolExecutor.execute执行
public class TestPool {
public static void main(String[] args) {
System.out.println(Runtime.getRuntime().availableProcessors());
//创建一个线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
2,
5,
2,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(5),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy()
);
try {
for(int i = 1; i <= 20; i++) {
int temp = i;
threadPoolExecutor.execute(()->{
try {
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName()+" = >" + temp);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}finally {
threadPoolExecutor.shutdown();
}
}
}
线程池设置最大线程数量公式:
(1)线程池大小 = CPU的数量 × 目标CPU的使用率 × (1+等待时间与计算时间的比)
(2)一般情况:
IO密集型应用,则线程池大小设置为 2N+1 (N为CPU数量,下同)
CPU密集型应用,则线程池大小设置为 N+1
IO密集型和 CPU密集型简单来说就是看服务器是注重CPU运算还是IO传输