- 线程池的优点
1. 降低资源消耗。通过重复利⽤已创建的线程降低线程创建和销毁造成的消耗。
2. 提⾼响应速度。当任务到达时,任务可以不需要的等到线程创建就能⽴即执⾏。
3. 提⾼线程的可管理性。线程是稀缺资源,如果⽆限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使⽤线程池可以进⾏统⼀的分配,调优和监控。 - 使用execute()和submit()方法 的区别
1. execute() ⽅法⽤于提交不需要返回值的任务,所以⽆法判断任务是否被线程池执⾏成功与否;
2. submit() ⽅法⽤于提交需要返回值的任务。线程池会返回⼀个 Future 类型的对象,通过这个 Future 对象可以判断任务是否执⾏成功,并且可以通过 Future 的 get() ⽅法来获取返回值, get() ⽅法会阻塞当前线程直到任务完成,。 - 线程池的实现原理:
- 判断线程池正在执行的线程数是否达到了核心线程数,如果没有则创建一个线程来执行任务。如果达到核心线程数,则进入下一个流程
- 判断当前工作队列是否已经满了,如果没有满则将新提交的任务存储在这个工作队列里,如果满了就进入下一个流程
- 判断当前线程池的线程数是否达到maximumPoolSize,如果没有,则创建一个救急线程来处理,如果满了就执行饱和策略
- ThreadPoolExecutor
- ThreadPoolExecutor构造方法:
- ThreadPoolExecutor构造方法:
- ThreadPoolExecutor的参数
1. corePoolSize。线程池的基本大小,核⼼线程数线程数定义了最⼩可以同时运⾏的线程数量。
2. maximumPoolSize。线程池允许创建的最大数量。如果队列满了,但创建的线程数小于maximumPoolSize,则线程池会创建新的线程执行任务,这个的前提是任务队列是有界的!!,maximumPoolSize会有一定的存活时间。
3. workQueue。任务队列,当线程执行的任务到达corePoolSize大小的时候,剩下的任务到来会先进入任务队列等待。可以选择如下几种任务队列:
ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO的原则对元素进行排序
LinkedBlockingQueue:基于链表结构的阻塞队列,按FIFO原则
PriorityBlockingQueue:具有优先级的无限阻塞队列
SynchronousQueue:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用溢出操作,否则就一直处于阻塞状态。
4. KeepAliveTime。救急线程在空闲状态下的等待时间,超过这个时间就销毁。
5. TimeUnit。时间单位。
6. ThreadFactory。用来设置线程的工厂。
7. RejectedExecutionHandler(饱和策略)。当队列和线程池都满了,对于接下来要到达的任务必须采取一种策略来处理提交。默认使用的是AbortPolicy,在无法接受新任务时抛出异常。
1. AbortPolicy。直接抛出异常。
2. CallerRunsPolicy。让调用者用自己的线程来执行任务
3. DiscardOldestPolicy。丢弃队列中最早未处理的一个任务并执行当前任务。
4. DiscardPolicy。不处理直接丢掉 - shutdown和shutdownNow的比较
1.shutdown不会接受新的任务但是会执行完已提交的任务。
2. shutdownNow,不会接受新的任务,且返回等待执行任务的列表,并用interrupt的方式打断线程
Executors
-
FixedThreadPool。
ExecutorService pool=Executors.newFixedThreadPool(int n)
固定大小的线程池,如果线程不空闲,就进入等待队列,等待队列是无限队列。LinkedBlockingQueue -
SingleThreadExecutor。
ExecutorService pool=Executors.newSingleThreadExecutor()
单线程的线程池,保证各个任务按顺序执行.LinkedBlockingQueue -
CachedThreadPool.
ExecutorService pool=Executors.newCachedThreadPool()
可根据实际情况调整线程数量的线程池,核心线程数是 0, 最大线程数是 Integer.MAX_VALUE,救急线程的空闲生存时间是 60s。SynchronousQueue -
怎么创建合适的线程?
1. 如果线程数过少导致程序不能充分利用系统资源,会导致线程池饥饿问题(因为线程太少导致任务无法处理)
2. 如果线程数过多会导致更多的线程上下文切换,占用更多的内存
3. CPU密集型运算:应该配置尽可能小的线程(CPU密集型任务提高CPU利用率,少的线程避免线程空闲)
4. I/O密集型运算:应该配置尽可能多的线程。(I/O密集型任务线程并不是一直在执行任务)
Fork/Join线程池
Fork/Join 体现的是一种分治思想,将大任务拆分成若干个小任务,最终汇总每个小任务的结果得到大任务的结果,Fork/Join 默认会创建与 cpu 核心数大小相同的线程池。
- fork方法的实现原理:当调用fork方法时,程序会调用PushTask异步执行这个任务然后立即返回结果,PushTask方法会把当前任务存放在
数组队列,然后去唤醒或者创建一个工作线程执行任务 - join方法的实现原理:join方法主要作用是阻塞当前线程并等待获取返回结果。首先查看任务状态,看任务是否被执行完成,如果执行完成就返回任务状态,没执行完就从任务数组中提取任务并执行。