原创

java线程池

线程池的好处

  • 降低资源消耗: 重复利用已创建的线程降低线程创建和销毁造成的消耗;
  • 提高响应速度: 不需要等到线程创建就能立即执行;
  • 线程的可控性: 使用线程池可以进行统一分配、调优和监控;

线程池原理

  
  1. 线程数量未达到 corePoolSize,则新建一个线程(核心线程)执行任务

  2. 线程数量达到了 corePools,则将任务移入队列等待

  3. 队列已满,新建线程(非核心线程)执行任务

  4. 队列已满,总线程数又达到了 maximumPoolSize,就会由拒绝策略抛出异常

线程池流程

  
  1. 线程数量小于核心线程数量则会进行核心线程的创建,不管其他线程当前是否处于空闲
  2. 线程数量大于等于核心线程数量则会把任务放入任务队列里面(先进先出),核心线程会循环去take队列里面的任务,获取不到任务则会进入阻塞状态,如果也对核心线程设置了超时的状态,则会在阻塞超时后进行销毁;
  3. 线程数量大于核心数量且任务队列已满,则会创建非核心线程,非核心线程执行完任务后会循环去take队列里面的任务,获取不到任务会进行阻塞状态,阻塞超时后进行销毁;
  4. 线程数量等于最大线程数量且任务队列已满,则会进行拒绝策略;

线程池使用



public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)

  • corePoolSize:线程池核心线程数(平时保留的线程数)
  • maximumPoolSize:线程池最大线程数(当workQueue都放不下时,启动新线程,最大线程数)
  • keepAliveTime:超出corePoolSize数量的线程的保留时间。
  • unit:keepAliveTime单位
  • workQueue:任务队列,存放来不及执行的线程
  • threadFactory:线程工厂
  • handler:拒绝策略

任务队列

  • ArrayBlockingQueue:构造函数一定要传大小

  • LinkedBlockingQueue:构造函数不传大小会默认为(Integer.MAX_VALUE ),当大量请求任务时,容易造成 内存耗尽。

  • SynchronousQueue:同步队列,一个没有存储空间的阻塞队列 ,将任务同步交付给工作线程。

  • PriorityBlockingQueue : 优先队列

拒绝策略

  • AbortPolicy(默认):直接抛弃

  • CallerRunsPolicy:用调用者的线程执行任务

  • DiscardOldestPolicy:抛弃队列中最久的任务

  • DiscardPolicy:抛弃当前任务

线程池状态

  • 运行状态:线程池创建好后就是该状态;
  • 关闭状态:调用shutdown就会变成该状态,此时线程池不再接收新的任务,但会把已存在的任务处理完;
  • 停止状态:调用shutdownNow变成该状态,线程池不再接收新的任务,同时会把存在的任务抛弃;
  • 整理状态:调用shutdown /shutdownnow后,活动线程都降为0后会到该状态,之后会调用销毁的方法;
  • 销毁状态:调用销毁方法后会到该状态;

其他问题

线程池数量如何选择?
首先要搞清楚任务的性质,是CPU密集型任务还是IO密集型任务,如果是CPU密集型任务可以配置cpu数量+1的线程,如果是IO密集型任务可以配置cpu数量*2
任务队列如何选择?
建议使用有界队列,有界队列增加系统的稳定性,采用无界队列会发生OMM的风险,
线程池监控的命令?
  • takeCount: 线程池需要执行的任务数量
  • completedTaskCount:线程池在运行过程中已完成的任务数量,小于或等于taskCount

  • largestPoolSize:线程池里曾经创建过的最大线程数量。通过这个数据可以知道线程池是否曾经满过。如该数值等于线程池的最大大小,则表示线程池曾经满过

  • getPoolSize:线程池的线程数量。如果线程池不销毁的话,线程池里的线程不会自动销毁,所以这个大小只增不减

  • getActiveCount:获取活动的线程数

正文到此结束