1.线程池的概述
一种线程使用模式,线程过多会带来调度开销。进而影响缓存局部性和整体性能,而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在短时间内创建与销毁线程的代价,线程池不仅能够保证内核的充分利用,还能防止过分调度
线程池的使用优势:线程池做的工作是控制运行中的线程数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了核心数量,超出数量的线程排队等候,等待其他线程执行完毕,再从队列中取出任务来执行
线程池的主要特点:
- 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗
- 提高响应速度:当任务到达时,任务可以不需要等待线程创建就能立即执行
- 提高线程的可管理性:线城是稀缺资源,如果无限制的创建,不仅会消耗系统资源还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
- Java中的线程池是通过Executor框架实现的,该框架中用到了Executor,Executors ExecutorService,ThreadPoolExecutor这几个类
2.线程池架构
3.线程池使用方式
使用方式一:
// 一池n线程
ExecutorService executorService = Executors.newFixedThreadPool(int n);
使用方式二:
// 一池一线程
ExecutorService executorService1 = Executors.newSingleThreadExecutor();
使用方式三:
// 一池可扩容线程
ExecutorService executorService2 = Executors.newCachedThreadPool();
5.线程池的七个参数
通过查看源码发现线程池的7个参数为
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler
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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
接下来我们分别来讲讲这七个参数的作用
corePoolSize:常驻线程池的线程数,该线程数小于最大线程数
maximumPoolSize: 线程池中的最大线程数,该线程数大于常驻线程数
keepAliveTime: 线程池中空闲线程等待工作的超时时间,一旦超过这个时间,该线程就 会被销毁
unit: 设置超时时间
workQueue:阻塞队列,即当线程池中的常驻线程全部在工作状态时,这时候有任务进来时,则会将这些任务放到阻塞队列,等待线程
threadFactory: 线程工厂,创建线程的工厂
handler:拒绝处理器,线程池有四种处理方式即AbortPolicy、DiscardPolicy、CallerRunsPolicy、DiscardOldestPolicy
6.线程池底层工作原理
先来一张我画的图
工作原理:
1.当我们创建了一个线程池并不意味着这个线程池里面就有了线程,当我们需要使用线程池里面的线程的时候,即开始执行execute()方法使,线程池才开始创建线程
2.当有任务进到这个线程池并开始寻找线程来执行任务时,首先寻找的是线程池中的corePool即上图的第一步
3.当corePool全部在工作,而此时又有新的任务进到线程池时,我们就需要把它们放入阻塞队列,等待其他的线程忙完再来执行他们,即上图中的第二步
4.当阻塞队列已满,且仍然有新的任务来到线程池寻找线程时,我们就会在线程池中新建线程,直到最大线程数即上图中的第三步
5.当线程池中的线程数达到了最大线程数时,且此时仍然有任务进入到线程池寻找线程,此时会触发线程池的拒绝处理策略即上图的第四步
线程池的四种拒绝策略:
1.AbortPolicy
丢弃任务并抛出RejectedExecutionException异常
2.DiscardPolicy
也是丢弃任务,但是不抛出异常
3.CallerRunsPolicy
由调用线程处理任务
4.DiscardOldestPolicy
丢弃队列最前面的任务,然后重新尝试执行任务(重复该过程)
7.自定义线程池(工作中使用的方法)(线程创建方式四)
我们来自定义一个线程池,代码如下
package com.company.pool;
import java.util.concurrent.*;
// 自定义线程池
public class ThreadPoolDemo1 {
public static void main(String[] args) {
// 自定义一个线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
2,
3,
5L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.
);
try{
// 从该线程池中取出线程来执行任务
for(int i = 1;i <= 10;i++) {
threadPoolExecutor.execute(() -> {
System.out.println(Thread.currentThread().getName() + "来处理业务了!!!");
});
}
}catch (Exception e){
e.printStackTrace();
}finally {
threadPoolExecutor.shutdown();
}
}
}