线程池的3大方法,7大参数,执行原理,拒绝策略
线程池的好处
1)降低资源的消耗 2)提高响应的速度 3)方便管理 4)线程复用、可以控制最大并发数、管理线程
1、三大方法:
Executors.newSingleThreadExecutor(); //单线程
Executors.newFixedThreadPool(5);// 固定线程个数
Executors.newCachedThreadPool(); //缓存池,可扩展,遇强则强,遇弱则弱,最大值为Integer的界,约21亿
注意:实际使用线程池最好不使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池
规则,规避资源耗尽的风险
说明: Executors 返回的线程池对象的弊端如下:
1) FixedThreadPool和SingleThreadPool:
允许的请求队列长度为Integer .MAX_ VALUE, 可能会堆积大量的请求,从而导致OOM
2) CachedThreadPool 和ScheduledThreadPool:
允许的创建线程数量为Integer .MAX_ VALUE,可能会创建大量的线程,从而导致OOM
2、七大参数:(自定义创建线程池)
public ThreadPoolExecutor(int corePoolSize, //常驻核心线程数 int maximumPoolSize, //最大线程数 long keepAliveTime, //空闲线程存活时间(数字) TimeUnit unit, //空闲线程存活时间(单位) BlockingQueue<Runnable> workQueue, //任务队列 ThreadFactory threadFactory, //线程工厂,一般不动 RejectedExecutionHandler handler); //拒绝策略
核心线程
线程池一创建号就会创建并开启这些线程
最大线程数的确定的方法
最大线程数为这个线程池能创建的最大线程数量
1)CPU密集型:看本机的逻辑CPU为几核最大线程数就为几
原因:因为这样可以达到最大并行的线程数,效率最高
附:Runtime.getRuntime().availableProcessors();//获取cpu核数
2)IO密集型:先判断程序中十分消耗IO的线程数量,最大线程数再乘以2
原因:IO操作耗时量大,并且占用资源也大,所以先分配同等数量的线程去执行这些IO线程,再分配大于该数量的线程,一般是2倍
空闲线程存活时间
除开核心线程数,剩余的线程过了设置的空闲线程存活时间后,会自杀,但核心线程你主动关闭是不会自杀的
任务队列
存放等待执行线程的队列,一般用BlockingQueue阻塞队列,除此之外还有BlockingDueue双端队列、SynchronousQueue同步队列等等
线程工厂
线程池的线程由线程工厂创建,一般默认Executors.defaultThreadFactory(),不改
拒绝策略
当线程数量大于最大线程数+任务队列时,所采取的策略
3、执行原理:
1)corePoolSize 个线程处理任务 2)多余的任务在任务队列中等待 3)任务队列满了之后,线程池的线程数会扩容到 maximumPoolSize 4)线程池最大承载能力满了(任务队列+maximumPoolSize),拒绝任务
举个形象好理解的例子
场景:银行排队办理业务,五个柜台,两个开着(1、2),三个暂时关闭(3、4、5),有三个候客区位置
人少的时候:1~5个人(小于核心线程数量+阻塞队列长度)一起来,则只使用已经开着的柜台(核心线程),剩下的在候客区等待并排队(阻塞队列),并且候客区按前来的先办理业务的顺序排队(队列的FIFO)
人多的时候:6~8个人(大于核心线程数量+阻塞队列长度,小于最大线程数+阻塞队列长度)一起来,这些线程通过操作系统时间片的轮转调度算法分配,先来的去1、2号柜台办理业务(核心线程),后来的在候客区等待并排队(阻塞队列),剩下的由于还有人,就会开启3、4、5暂时关闭的柜台(最大线程-核心线程),来给客户办理业务
人超级多的时候:大于8个人(大于最大线程数量+阻塞队列长度)一起来,由于像上种情况,已经满功率进行招待了,还是不够,则除了先来的8个人,剩余的人则被拒之门外(拒绝策略)
4、拒绝策略:
AbortPolicy(默认):直接抛出RejectedExecutionException异常阻止系统正常运行。
银行满了(大于最大线程数+阻塞队列),还有人进来,不处理这个人的,抛出异常!
DiscardPolicy:丢弃无法处理的任务,不会抛出异常。(如果允许任务丢失,这是最好的一种策略)
银行满了(大于最大线程数+阻塞队列),还有人进来,不处理这个人的,不会抛出异常!
DiscardOldestPolicy:抛弃队列中等待最久的任务,重新提交当前任务。
银行满了(大于最大线程数+阻塞队列),尝试去和最早的竞争(第一个办理业务的人),也不会抛出异常!
CallerRunsPolicy(调用者运行):由调用线程(提交任务的线程)处理该任务。
银行满了(大于最大线程数+阻塞队列),哪来的去哪里(从main线程来的就该给main线程自己去处理)!