【为什么要使用线程池】
------传统线程创建方式的问题
- 反复创建线程系统开销比较大,每个线程创建和销毁都需要时间,如果任务比较简单,那么就有可能导致创建和销毁线程消耗的资源比线程执行任务本身消耗的资源还要大。
- 过多的线程会占用过多的内存等资源,还会带来过多的上下文切换,同时还会导致系统不稳定。
------线程池的优点
- 线程池可以解决线程生命周期的系统开销问题,同时因为线程复用,消除了创建线程的过程,可以加快响应速度。
- 线程池会根据配置和任务数量灵活地控制线程数量,可以统筹内存和 CPU 的使用,避免资源使用不当。
- 线程池可以统一管理资源。
【线程池参数】
------Spring配置:
------线程池基本工作流:
- 应用初始化后,当前线程池线程数为0;
- 有任务被提交后,创建核心线程执行任务;
- 当核心线程数达到上限后,任务添加到线程队列;
- 当线程队列内任务数达到上限后,创建非核心线程执行任务;
- 当线程池内总线程数达到最大线程数后,任务会被拒绝;
- 当任务渐渐被执行完,队列为空,而且当前线程数大于核心线程数之后,线程池会检测线程的keepAliveSeconds,如果线程空闲时间大于这个时间,线程将会被销毁。
- 线程渐渐地被销毁,线程池内存活线程等于核心线程数之后,则不继续做处理,线程空转,等待新的任务到来。
------ThreadFactory
线程工厂,主要作用是生产线程。
我们可以选择使用默认的线程工厂,创建的线程都会在同一个线程组,并拥有一样的优先级。
也可以选择自己定制线程工厂,以方便给线程自定义命名。
【拒绝策略】
【六种常见的线程池】
------FixedThreadPool
核心线程数和最大线程数一样,所以是固定线程数的线程池。
------CachedThreadPool
可缓存线程池,核心线程数为0,最大线程数为int最大值。
队列的容量为0,实际不存储任何任务,它只负责对任务进行中转和传递,所以效率比较高。
------ScheduledThreadPool
支持定时或周期性执行任务。
------SingleThreadExecutor
使用唯一的线程去执行任务,可保证执行顺序。
------SingleThreadScheduledExecutor
和第三个类似,只不过线程只有一个。
------ForkJoinPool
实现任务的分裂和汇总,充分利用多核CPU的计算能力。
【阻塞队列】
------LinkedBlockingQueue
无界队列,FixedThreadPool 和 SingleThreadExector使用。
------SynchronousQueue
SynchronousQueue是一个没有数据缓冲的BlockingQueue,生产者线程对其的插入操作put必须等待消费者的移除操作take,反过来也一样。
生产者和消费者互相等待对方,握手,然后一起离开。
对应线程池 CachedThreadPool。
------DelayedWorkQueue
对应线程池ScheduledThreadPool 和 SingleThreadScheduledExecutor。
DelayedWorkQueue 的特点是内部元素并不是按照放入的时间排序,而是会按照延迟的时间长短对任务进行排序,内部采用的是"堆"的数据结构。
【关闭线程池】
------shutdown()
安全地关闭一个线程池。
并不是立刻就被关闭,调用 shutdown() 方法后线程池会在执行完正在执行的任务和队列中等待的任务后才彻底关闭。
------isShutdown()
可以返回 true 或者 false 来判断线程池是否已经开始了关闭工作。
------isTerminated()
可以检测线程池是否真正"终结"了,这不仅代表线程池已关闭,同时代表线程池中的所有任务都已经都执行完毕了。
------awaitTermination()
判断线程池状态。
调用 awaitTermination 方法后当前线程会尝试等待一段指定的时间,如果在等待时间内,线程池已关闭并且内部的任务都执行完毕了,也就是说线程池真正"终结"了,那么方法就返回 true,否则超时返回 fasle。
------shutdownNow()
会给所有线程池中的线程发送 interrupt 中断信号,尝试中断这些任务的执行;
然后会将任务队列中正在等待的所有任务转移到一个 List 中并返回,我们可以根据返回的任务 List 来进行一些补救的操作,例如记录在案并在后期重试。
【线程复用原理】
在线程池中,同一个线程从BlockingQueue里面不断地提取任务;
核心内容在于对Tread进行了封装,每个线程都会去执行一个循环任务;
这个循环任务会不停地检查队列里面是否有待执行的任务,如果有,则执行其run方法,这样,线程就被串联了起来。