线程池的创建和使用,线程池源码初探(篇一)


今天来总结一下线程池的使用和几个重要参数吧,本来自己业务并没有用到线程池。但是线程池的使用场景还是挺广泛的。特别是高并发、多线程的情况下。最近也尝试了一下jump cao,也是个常遇到个考点吧。就当自我总结,做个笔记了。


1、线程池的介绍

  • 线程池是一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。

2、线程池的优点:

  • a、线程池可以降低系统资源消耗,通过一次创建多个线程放在线程池中,当任务来临时。重用已存在的线程,可以降低线程创建和销毁造成的消耗。
  • b、提高系统响应速度,当有任务到达时,直接可调用已创建的线程,节省线程创建造成的时间损耗。
  • c、可以方便线程并发数的管控。当任务过多,线程无限制的创建,可能会导致内存占用过多而产生OOM,而且还会造成cpu过度切换,而cpu切换线程是有时间成本的(需要保持当前执行线程的现场,并恢复要执行线程的现场)。
  • d、还可以设置线程池定时启动等功能。

3、主要参数介绍

  • java.uitl.concurrent.ThreadPoolExecutor类是线程池中最核心的一个类,因此如果要透彻地了解Java中的线程池,必须先了解这个类。可以先看一下ThreadPoolExecutor 类源码里的这四个方法:
    在这里插入图片描述
public class ThreadPoolExecutor extends AbstractExecutorService {
    .....
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue);
 
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory);
 
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler);
 
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);
    ...
}

可以看到后面三个都是对第一个方法的重载,所以最核心的几个参数就是corePoolSize:核心线程数、maximumPoolSize:最大线程数、keepAliveTime:线程存活时间、unit:时间单位、workQueue:阻塞队列…

a、corePoolSize:核心线程数
  • 这个参数表示的是线程创建的时候默认创建的线程数,会预创建这么多条线程先放在里面。即使当前没有任务、也会创建这么多线程。
b、maximumPoolSize:最大线程数
  • 线程池最大线程数。如果线程数量少于线程最大数且大于核心线程数量的时候,只有当阻塞队列满了才创建新线程。当线程数量大于最大线程数且阻塞队列满了这时候就会执行一些策略来响应该线程。
c、keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。
  • 默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0。
d、unit:参数keepAliveTime的时间单位
  • unit有7种取值,在TimeUnit类中有7种静态属性:
	TimeUnit.DAYS;               //天
	TimeUnit.HOURS;             //小时
	TimeUnit.MINUTES;           //分钟
	TimeUnit.SECONDS;           //秒
	TimeUnit.MILLISECONDS;      //毫秒
	TimeUnit.MICROSECONDS;      //微妙
	TimeUnit.NANOSECONDS;       //纳秒
e、workQueue:阻塞队列
  • 用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择:
   	ArrayBlockingQueue;
   	LinkedBlockingQueue;
   	SynchronousQueue;

说都说到这儿了,继续把剩下两个介绍了吧。

f、threadFactory:线程工厂,主要用来创建线程。
g、handler: 表示当拒绝处理任务时的策略,有以下四种取值:

	ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
	 
	ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 
	
	ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)

	ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务  
	

4、线程池的五种状态

  • RUNNING: 在这个状态的线程池能判断接受新提交的任务,并且也能处理阻塞队列中的任务
  • SHUTDOWN: 处于关闭的状态,该线程池不能接受新提交的任务,但是可以处理阻塞队列中已经保存的任务,在线程处于RUNNING状态,调用shutdown()方法能切换为该状态。
  • STOP: 线程池处于该状态时既不能接受新的任务也不能处理阻塞队列中的任务,并且能中断现在线程中的任务。当线程处于RUNNING和SHUTDOWN状态,调用shutdownNow()方法就可以使线程变为该状态
  • TIDYING: 在SHUTDOWN状态下阻塞队列为空,且线程中的工作线程数量为0就会进入该状态,当在STOP状态下时,只要线程中的工作线程数量为0就会进入该状态。
  • TERMINATED: 在TIDYING状态下调用terminated()方法就会进入该状态。可以认为该状态是最终的终止状态。

5、线程池常用方法

  • submit():提交任务,能够返回执行结果execute+Future

  • shutdown():关闭线程池,等待任务都执行完

  • shutdownNow():关闭线程池,不等待任务执行完

  • getTaskCount():线程池已执行和未执行的任务总数

  • getCompletedTaskCount():已完成的任务数量

  • getPoolSize():线程池当前线程数量

  • getActiveCount():当前线程池中正在执行任务的线程数量


注: 由于博主业务切实没涉及到,之前也就只在安卓开发时常用到多线程。安卓那个东西线程一言不合就闪退。由于没有高并发需求,所以线程池这块也是自己的盲区,简单总结下概念问题。后续有时间在研究吧。如有理解错误的地方,还望路过的大佬指正!


我摊牌了,我就是来嫖1024勋章的,哈哈…但是也有认真在总结。好!外卖到了,就今天先写到这儿!祝大家节日快乐!哈哈哈!

©️2020 CSDN 皮肤主题: 黑客帝国 设计师:上身试试 返回首页