线程池的三大方法、七大参数、四种拒绝策略
1、什么是线程池
线程池:三大方法、七大参数、四种拒绝策略
OOM:Out of memory 内存溢出
池化技术
程序的运行,本质:占用系统的资源!优化资源的使用!=>池化技术
线程池、连接池、内存池、对象池、常量池 //… 创建、销毁、
池化技术:事先准备好一些资源,有人要用,就来我这里拿,用完之后还给我
线程池的好处:
- 降低资源的消耗
- 提高响应的速度
- 方便管理
线程复用、控制最大并发数、管理线程
强制----线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
说明:线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源不足的问题。
如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。
2、线程池的使用规范
线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors 返回的线程池对象的弊端如下:
1) FixedThreadPool 和 SingleThreadPool:
允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
2) CachedThreadPool:
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
3、 线程池的三大方法
三大方法
-
单一线程 (只能有一个线程在执行)
-
固定线程(设置最大可执行的线程数)
-
可伸缩线程(可多可少,根据电脑配置而定,本电脑最多只有32个可同时运行线程)
newCachedThreadPool (ThreadPoolExecutor)
创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
newFixedThreadPool (ThreadPoolExecutor)
创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
newSingleThreadExecutor (ThreadPoolExecutor)
创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
以下线程池创建补充
newScheduledThreadPool (ScheduledThreadPoolExecutor)创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
newSingleThreadScheduledExecutor (ScheduledThreadPoolExecutor)
创建一个单线程用于定时以及周期性执行任务的需求。
newWorkStealingPool (1.8 ForkJoinPool)
创建一个工作窃取
注:如果使用Executors创建线程池,会造成内存溢出问题,因为最大线程数默认为Interger.Valueof() 接近21亿
package com.shuang.ThreadPool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Executors工具类:3大方法
* <p>
* 使用了线程池之后,使用线程池创建线程
*/
public class Demo01 {
public static void main(String[] args) {
// ExecutorService threadPool = Executors.newFixedThreadPool(5);//创建一个最大线程数为5的线程池
ExecutorService threadPool = Executors.newCachedThreadPool();
// ExecutorService threadPool = Executors.newSingleThreadExecutor();//单个线程
/*Executors.newFixedThreadPool(5); // 创建一个固定的线程池的大小
Executors.newCachedThreadPool(); // 可伸缩,遇强则强,遇弱则弱*/
try {
for (int i = 0; i < 10; i++) {
//线程池创建线程
//threadPool.execute(参数为Runnable)
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName().toString() + "--->ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 线程池用完,程序结束,关闭线程
threadPool.shutdown();
}
}
}
4、线程池的七大参数
七大参数
源码分析
本质还是使用ThreadPoolExecutor
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 5, 3, ,TimeUnit.SECONDS,new LinkedBlockingDeque<>(3),Executors.defaultThreadFactory(),拒绝策略)
参数一:核心线程数
参数二:最大线程数,在我们通过线程池获取线程数<=最大线程数时,只会使用核心线程数中的线程,比如上面这个参数,核心线程数为2,最大线程数为5,当我们从线程池中获取的线程数<=5时,只会使用2个核心线程数去交替完成
参数三:超时等待时间
参数四:超时等待时间的单位
参数五:阻塞队列,使用数组或者链表都可以,参数为3,可以理解为如果我们一次性从线程池获取线程数>最大线程数+阻塞队列大小,就需要使用到四大拒绝策略
参数六:线程工厂
参数七:拒绝策略
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1, // 核心线程数是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 static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, // 约等于21亿 ,OOM 内存溢出
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
// 本质ThreadPoolExecutor()
// 线程池的七大参数
public ThreadPoolExecutor(int corePoolSize, // 核心线程池大小
int maximumPoolSize, // 最大核心线程池大小
long keepAliveTime, //超时了没有人调用就会释放
TimeUnit unit, // 超时单位
BlockingQueue<Runnable> workQueue, // 阻塞队列
ThreadFactory threadFactory, // 线程工程创建线程,一般不用动
RejectedExecutionHandler handler // 拒绝策略
) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
5、手动创建一个线程
为什么要手动创建一个线程
强制线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更佳明确线程池的运行规则,规避资源耗尽的风险。
说明:Executors返回线程池对象的弊端如下:
-
FixedThreadPool
和SingleThreadPool
:允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM(内存溢出)
-
CachedThreadPool
和ScheduledThreadPool
:允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM
import java.util.concurrent.*;
/**
* Executors工具类:3大方法
* <p>
* 使用了线程池之后,使用线程池创建线程
*/
public class Demo02 {
public static void main(String[] args) {
// 自定义线程池! 工作 ThreadPoolExecutor
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 5, 3, TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3), // 候客区
Executors.defaultThreadFactory(), // 默认线程工厂
new ThreadPoolExecutor.AbortPolicy() // 默认的拒绝策略,银行满了还有人进来,不处理,抛出异常
// new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略,哪来的去哪里
// new ThreadPoolExecutor.DiscardPolicy() // 拒绝策略, 队列满了不会抛出异常
// new ThreadPoolExecutor.DiscardOldestPolicy() // 拒绝策略,队列满了,尝试去和最早的竞争,不会抛出异常
);
try {
// 最大承载:Queue + max
// 超过最大承载抛出异常:RejectedExecutionException
for (int i = 1; i <= 9; i++) {
//线程池创建线程
//threadPool.execute(参数为Runnable)
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName().toString() + "--->ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 线程池用完,程序结束,关闭线程
threadPool.shutdown();
}
}
}
总结:
当创建的线程数超过最大线程数,超出多少个就触发多少个最大线程,如果超过了最大承载数,则会执行拒绝策略
最大承载:core+ max
超过最大承载抛出异常:RejectedExecutionException
6、四种拒绝策略
new ThreadPoolExecutor.AbortPolicy() // 默认的拒绝策略,银行满了还有人进来,不处理,抛出异常
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略,哪来的去哪里
new ThreadPoolExecutor.DiscardPolicy() // 拒绝策略, 队列满了不会抛出异常
new ThreadPoolExecutor.DiscardOldestPolicy()
// 拒绝策略,队列满了,尝试去和最早的竞争,不会抛出异常
第一种拒绝策略 AbortPolicy
第二种拒绝策略 CallerRunsPolicy
7、小结和拓展
线程池的大小该如何去设置!
了解:IO密集型,CPU密集型:(调优)
最大线程到底该如何定义:
1、CPU 密集型 几核,就定义为几,可以保证CPU效率最高!
2、IO 密集型 > 判断你的程序中十分耗IO的线程
程序 大型任务 io十分占用资源!
public class Demo02 {
public static void main(String[] args) {
// 自定义线程池! 工作 ThreadPoolExecutor
/**
* 最大线程到底该如何定义:
* 1、CPU 密集型 几核,就定义为几,可以保证CPU效率最高!
* 2、IO 密集型 判断你的程序中十分耗IO的线程
* 程序 15个大型任务 io十分占用资源!
*/
//获取CPU的核数
System.out.println(Runtime.getRuntime().availableProcessors());
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2,
5,
3,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(3), // 候客区
Executors.defaultThreadFactory(), // 默认线程工厂
new ThreadPoolExecutor.AbortPolicy() // 默认的拒绝策略,银行满了还有人进来,不处理,抛出异常
// new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略,哪来的去哪里
// new ThreadPoolExecutor.DiscardPolicy() // 拒绝策略, 队列满了不会抛出异常
// new ThreadPoolExecutor.DiscardOldestPolicy() // 拒绝策略,队列满了,尝试去和最早的竞争,不会抛出异常
);
try {
// 最大承载:Queue + max
// 超过最大承载抛出异常:RejectedExecutionException
for (int i = 1; i <= 9; i++) {
//线程池创建线程
//threadPool.execute(参数为Runnable)
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName().toString() + "--->ok");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 线程池用完,程序结束,关闭线程
threadPool.shutdown();
}
}
}