线程池的运行过程
1:当一个任务提交时,如果CorePool
中的核心线程少于CorePoolSize
,则创建一个新线程执行任务(需要全局锁)
2:如果CorePool中没有空闲的线程,那么加入BlockingQueue等待核心线程拉取任务执行
3:如果BlockingQueue已满,创建新线程后如果大于maximumPoolSize
就跳转到4拒绝执行任务,如果小于就创建新线程执行任务(需要全局锁)
4:四种不同的拒绝策略,通过rejectedExecution()
方法执行。
需要获取全局锁导致线程池的性能大大下降,应该尽量避免产生步骤1和步骤3
使用Executors创建线程池 :但一般都不建议这么创建线程池
package pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* alibab开发手册以及工作中都是用 ThreadPoolExecutor 创建线程池,
* 而不是用三大方法 newSingleThreadExecutor,newFixedThreadPool,newCachedThreadPool这三种
*/
public class Demo01 {
public static void main(String[] args) {
// 单个线程
ExecutorService threadpool = Executors.newSingleThreadExecutor();
// 固定线程池大小
//ExecutorService threadpool = Executors.newFixedThreadPool(5);
// 可伸缩的,遇强则强,遇弱则弱
ExecutorService threadpool = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
threadpool.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
threadpool.shutdown();
}
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService(
new ThreadPoolExecutor(1,
1,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()
)
);
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads,
nThreads,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()
);
}
public LinkedBlockingQueue() {
this(2147483647);
}
从源码中可以看出,newSingleThreadExecutor只允许创建单个线程,其他的线程都会被放在 LinkedBlockingQueue 阻塞队列中,而 newFixedThreadPool 能够创建自定义数量的线程数,同时,两种线程池都使用 LinkedBlockingQueue 阻塞队列,而阻塞队列的长度为 Integer.MAX_VALUE = 2147483647 ,过多的创建线程可能会造成内存溢出,也就是 OOM。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0,
Integer.MAX_VALUE,
60L,
TimeUnit.SECONDS,
new SynchronousQueue<Runnable>()
);
}
而在 newCachedThreadPool 线程池中将最大的线程池数量设置为 Integer.MAX_VALUE ,过多的创建线程的话也会造成一个 内存溢出,OOM。所以阿里云开发手册明确指出不建议使用这三种方式创建线程池,而是采取了自定义七大参数的方式创建线程池。
七大参数的设置:
package pool;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Demo02 {
public static void main(String[] args) {
// corePoolSize核心线程池,默认开启的线程池数量
// maximumPoolSize 最大线程池,
// keepAliveTime + TimeUnit.SECONDS 除核心线程池以外的3个线程池空闲时,保持 3 秒无别的线程后关闭
// ArrayBlockingQueue 阻塞队列候客区
// ThreadFactory线程工厂一般设为默认值不用改
// 线程池 4 大策略之一 超过 maximumPoolSize + 阻塞队列=线程最大承载时触发
// 1.AbortPolicy 报错 concurrent.RejectedExecutionException
// 2.CallerRunsPolicy 哪来的去哪里,打发到 main 线程
// 3.DiscardOldestPolicy 即使线程数超过最大承载,也不会抛出异常,丢掉任务
// 4.DiscardPolicy 队列满了尝试会与最早的线程竞争,同时不会抛出异常
//池的最大大小如何设置(maximumPoolSize)
//1.cpu 密集型,电脑或者处理器是几核就设置为几,保持cpu效率最高====6核12线程(设为12动态获取)
//2.IO 密集型 根据 十分占用 io 资源的任务数决定。一般设为 2倍 任务数
System.out.println(Runtime.getRuntime().availableProcessors());
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
2,
5,
3,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(3),
Executors.defaultThreadFactory(),
//new ThreadPoolExecutor.AbortPolicy()
//new ThreadPoolExecutor.CallerRunsPolicy()
//new ThreadPoolExecutor.DiscardOldestPolicy()
new ThreadPoolExecutor.DiscardPolicy()
);
// 线程最大承载: queue + max
for (int i = 0; i < 9; i++) {
threadPoolExecutor.execute(()->{
System.out.println(Thread.currentThread().getName()+" ok");
});
}
threadPoolExecutor.shutdown();
}
}
corePoolSize核心线程池,默认开启的线程池数量
maximumPoolSize 最大线程池,
keepAliveTime + TimeUnit.SECONDS 除核心线程池以外的其他线程池空闲时,保持 3 秒(时间自己定义)无别的线程进来,3秒后关闭除核心线程池外的线程
ArrayBlockingQueue 阻塞队列候客区
ThreadFactory线程工厂一般设为默认值不用改
阻塞队列
阻塞队列-----四组api
四大策略
线程池 4 大策略, 超过 maximumPoolSize + 阻塞队列=线程最大承载时触发,线程数不达到最大承载时不会执行4个策略
以下是4个策略分别执行的操作
1.AbortPolicy 报错 concurrent.RejectedExecutionException
2.CallerRunsPolicy 哪来的去哪里,若为主线程,打发到 main 线程
3.DiscardOldestPolicy 即使线程数超过最大承载,也不会抛出异常,丢掉任务
4.DiscardPolicy 队列满了尝试会与最早的线程竞争,同时不会抛出异常
线程池的5种运行状态
Running状态下添加处理线程
Shutdown状态下不再接受新的线程,但仍会处理阻塞队列和线程池中的线程
Stop状态下会中断正在处理的线程,不处理阻塞队列和其他
Tidying状态下所有线程执行完毕,ctl记录的线程数为0
Terminated状态下线程池终止
最后简单介绍一下
池的最大大小如何设置(即maximumPoolSize参数的设置)
1. CPU 密集型,电脑或者处理器是几核就设置为几,保持cpu效率最高====如我的电脑为6核12线程(设为12)
动态获取:
Runtime.getRuntime().availableProcessors()
2. IO 密集型 根据 十分占用 io 资源的任务数决定。一般设为 2倍 密集任务数