线程池
线程池的好处:
- 降低资源的消耗
- 提高响应的速度
- 方便管理。
线程复用、可以控制最大并发数、管理线程
三大创建方法
ExecutorService threadPool = Executors.newSingleThreadExecutor();
// 单个线程ExecutorService threadPool = Executors.newFixedThreadPool(10);
// 创建一 个固定的线程池的大小ExecutorService threadPool = Executors.newCachedThreadPool();
// 由执行的线程数决定大小。
具体过程:创建线程池对象—>执行---->关闭线程池
try {
for (int i = 1; i <= 10; i++) {
// 使用了线程池之后,使用线程池来创建线程
threadPool.execute(()->{
//执行的线程任务
System.out.println(Thread.currentThread().getName());
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
七大参数
一般开发中使用线程池最好不使用Executors去创建对象。如阿里巴巴开发手册所说的:OOM(内存溢出)
所以应该使用ThreadPoolExecutor来创建线程池
查看源码得到以下:
- 当执行线程数大于核心线程锁时,其它休眠的线程位置将会启用,最多不超过最大线程数。
- 当超过最大线程锁的线程任务会在阻塞队列中进行等待。
- 当此时并发执行的线程任务超过了最大线程数+阻塞队列长度时,拒绝策略起效。
拒绝策略有四种
new ThreadPoolExecutor.AbortPolicy()
// 同时并发的线程数大于最大线程数+阻塞队列长度时,不处理超过部分的线程任务,直接抛出异常。new ThreadPoolExecutor.DiscardPolicy()
//同时并发的线程数大于最大线程数+阻塞队列长度时,直接丢掉超过部分的线程任务,不抛出异常。new ThreadPoolExecutor.CallerRunsPolicy()
// 同时并发的线程数大于最大线程数+阻塞队列长度时,超过部分的线程任务回归main主线程执行。new ThreadPoolExecutor.DiscardOldestPolicy()
//阻塞队列满了,尝试去和最先执行的线程发起竞争,也不会抛出异常!
源码中的是这样的:
public ThreadPoolExecutor(int corePoolSize, //核心线程数
int maximumPoolSize, //最大线程数。最多几个线程并发。
long keepAliveTime, //当线程无任务时,几秒后结束该线程
TimeUnit unit,//线程结束的时间单位
BlockingQueue<Runnable> workQueue,//阻塞队列,限制等候线程数
ThreadFactory threadFactory,//线程工厂
RejectedExecutionHandler handler)//拒绝策略
举个创建的例子:
ExecutorService threadPool = new ThreadPoolExecutor(
2,
Runtime.getRuntime().availableProcessors(),
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.DiscardOldestPolicy()); //队列满了,尝试去和最早的竞争,也不会抛出异常!
如何选择线程池的大小
-
IO 密集型 :判断你程序中十分耗IO的线程,有n个,线程池就设置(2n)个
-
CPU 密集型:一般电脑几核,线程池就设置几个线程,可以保持cpu的效率最高!可以利用
Runtime.getRuntime().availableProcessors()
获取当前主机的核数。