线程池,是指管理一组同构工作线程的资源池,对一组线程进行统一的分配、监控、管理;线程池通过重用使用线程而不是创建新线程,可以在处理多个请求时,分摊使用直接创建和销毁线程产生的巨大资源开销。
线程池的核心参数有 核心线程数、最大线程数、非核心线程的存活时间、时间单位、阻塞工作队列、拒绝策略、ThreadFactory(线程工厂)。
核心线程数:可理解为线程池可维护的最小线程数量,核心线程创建后不会被回收,但大于核心数量的线程,在超过存活时间后会被回收。
最大线程数:线程池可容纳(允许创建)的最大线程数量。(核心线程数+非核心线程数)
非核心线程的存活时间:非核心线程的空闲时间超过该存活时间,就会被回收。
时间单位:存活时间的单位。
阻塞工作队列:是用来存储等待执行任务的队列。
拒绝策略:线程池的核心线程和非核心线程耗尽,工作队列也满了的时候,如果有新提交的任务,则直接采用拒绝策略。默认的拒绝策略是:ThreadPoolExecutor.AbortPolicy,
丢弃任务并抛出。一共4个拒绝策略,还有三个分别是 丢弃任务不抛出异常、丢弃队列中最末尾的任务(也就是最早进入队列的最旧的任务)、由原调用线程执行该任务(谁调用,谁执行).
ThreadFactory(线程工厂):主要用于创建线程,默认的工厂是defaultThreadFactory。
提交一个线程任务时,线程池会分配一个空闲线程,用于执行线程任务。
如果线程池中不存在空闲线程,那么就判断“当前存活的线程数量”是否小于核心线程数量,如果小于核心线程数,则创建一个核心线程去处理提交的线程任务。
如果线程池的线程数量大于核心线程数,则线程池会判断工作队列是否已满,若工作队列未满,则将该任务放入工作队列,等待线程池从工作队列取出别执行。
如果工作队列已满,则判断线程池的当前线程数是否达到最大线程数,如果未达到最大线程数,则创建一个非核心线程来执行提交的任务;若达到线程池的最大线程数量,还有新任务需要提交,则直接执行拒绝策略。
综上:线程池的执行顺序是:核心线程、工作队列、非核心线程。
常见线程池:
FixedThreadPool(线程数固定的线程池)使用的队列是无界队列,LinkedBlockingQueue。使用场景:适用于CPU密集型任务,确保CPU在长期被工作线程使用情况下,尽可能少的分配线程,即适用于执行长期的任务。
CachedThreadPool(线程数根据任务动态调整的线程池)使用的队列是,同步队列,队列容量未0;使用场景:用于并发执行大量短期的小任务。
SingleThreadPool(单一线程的线程池,只有一个线程)使用的队列是:无界队列,使用场景:适用于串行执行任务的场景,将任务按顺序执行。
ScheduledThreadPool(能执行定时、周期性任务的线程池)使用的队列是:DelayedWorkQueue基于堆栈结构的延迟队列,基于数组实现,初始容量为16的队列,按照延迟时间排序。使用场景:周期性执行,且需要限制线程数量的场景。
因为Java支持多线程开发,也就是支持多个任务并行,但频繁的创建销毁线程比较消耗性能,通过线程池,我们可以重复使用线程,减少性能的开销。
方便线程并发数的管理,提高响应速度,通过调用线程池的线程,从而提高响应速度。
多个线程运行过程中,都需要获取对方线程所持有的锁,导致长期处于无限等待的状态。
ReentrantLock和synchronized都是可重入锁(一个线程可以多次获取同一个锁);主要区别是ReentrantLock可以尝试获取锁,如果没有获取到,就可以进行别的操作,而不是无限等待下去造成死锁,更灵活;但synchronized是抢占模型,没获取到会一直等待;除了这个区别,synchronized是java提供的语法,使用后自动释放,但ReentrantLock是java代码实现的锁,使用后需要手动释放锁且支持公平锁。
乐观锁:乐观的认为在读取数据或资源时,不会被别人修改,锁机制较为宽松。
悲观锁:总认为数据会被别人修改,所以悲观锁在释放前,拒绝其他线程访问,悲观锁有强烈的独占和排他特性。
在JDK1.6之前,sychronized被称为重量级锁;JDK1.6之后,为了减少获得锁和释放锁带来的性能开销,引入了偏向锁和轻量级锁。底层是通过监视器monitor实现,线程通过执行monitorentry尝试获取monitor的所有权;当monitor被占用,就会处于锁定状态。默认使用偏斜锁,偏斜锁只适用于单线程的情况下,一旦出现其他线程,就会转换成轻量级锁,适用于多线程串行,一旦出现并发情况,就会进一步升级成重量级锁。
资源互斥:对锁分配的资源进行排他性控制,锁在同一时刻只能被一个线程使用
不可剥夺:线程已获得的资源在未使用完成之前,不可被剥夺。只能等待占有者自己释放。
请求等待:线程因请求资源阻塞时,线程对已经持有的资源保持不释放。
循环等待:线程之间的相互等待