目录
线程池的基本定义:
线程池特点:线程复用,控制最大并发数,管理线程
- 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗
- 提高相应速度,当任务到达时,任务可以不需要等到线程创建,就能立即执行。
- 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,,使用线程池可以进行统一的分配,调优和监控
线程池状态:
状态名 | 说明 |
RUNNING | |
SHUTDOWN | 不会接收新任务,但会处理阻塞队列剩余 任务 |
STOP | 会中断正在执行的任务,并抛弃阻塞队列 任务 |
TIDYING | 任务全执行完毕,活动线程为 0 即将进入 终结 |
TERMINATED | 终结状态 |
线程池的构造方法及七大参数:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
参数详解:
- corePoolSize 核心线程数目 (最多保留的线程数)
- maximumPoolSize 最大线程数目(核心线程+救急线程总共的数目)
- keepAliveTime 生存时间 -(针对救急线程 )
- unit 时间单位 (针对救急线程)
- workQueue 阻塞队列
- threadFactory 线程工厂 - 可以为线程创建时起个好名字
- handler 拒绝策略--4种
线程的拒绝策略:
AbortPolicy | 让调用者抛出 RejectedExecutionException 异常。(默认策略) |
CallerRunsPolicy | 让调用者运行任务。(在主线程中调用ThreadPool.submit ,采用此策略会让主线程来自行运行内容) |
DiscardPolicy | 放弃本次任务,不处理也不抛异常。 |
DiscardOldestPolicy | 放弃队列中最早的任务,然后把当前任务加入队列中尝试再次提交当前任务。简称:本任务取而代之。 |
线程池运行步骤:
- 创建线程池,等待提交过来的任务请求
- 调用execute()方法添加一个请求任务时,会有一下情况
- 如果正在运行的线程数量 < corePoolSize,那么马上创建线程运行这个任务。(所以线程是懒惰创建的,只有提交了任务才会创建线程)
- 如果正在运行的线程数量 >= corePoolSize,将这个任务放入队列中。(要求队列必须是有界的)
- 如果这个时候队列满了,且正在运行的线程数量 < maximumPoolSize,那么开始创建急救线程来执行这个任务。(救急线程处理新来的任务,而不是队列中已等待的数据)
- 如果这个时候队列满了,且正在运行的线程数量 >= maximumPoolSize,那么线程池执行设定好的拒绝策略来执行。
- 当线程完成任务时,会从队列中取下一个任务来执行。
- 当一个线程休息的时间超过一定的时间(keepAliveTime ),线程池会判断如果当前的时间 >= corePoolSize ,那么这个线程就会被停掉。
线程池的具体介绍
1.newFixedThreadPool
创建一个固定大小的线程池。
适合任务量已知,相对耗时的任务
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
特点:
- keepAliveTime为0
- corePoolSize = maximumPoolSize ,
- 阻塞队列是:LinkedBlockingQueue。是无界的,可以放任意数量的任务。
实例代码:
ExecutorService threadPool = Executors.newFixedThreadPool(5)
for (int i = 0; i < 10; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"\t 处理业务");
});
}
结果:
2.newCachedThreadPool
可缓存线程的线程池。若线程数超过任务所需,那么多余的线程会被缓存一段时间后才被回收,若线程数不够,则会新建线程。
适合任务数比较密集,但每个任务执行时间较短的情况。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
特点:
- 核心线程数是 0, 最大线程数是 Integer.MAX_VALUE
- 救急线程的空闲生存时间是 60s,意味着全部都是救急线程(60s 后可以回收)。
- 队列采用了 SynchronousQueue 实现特点是,它没有容量,没有线程来取是放不进去的
- 如果没有空闲线程,那么就创建一个新的线程,反之使用旧的线程执行任务。
实例代码:
private static void ThreadPoolInit() {
ExecutorService threadPool = Executors.newCachedThreadPool(); //一池n个处理线程
for (int i = 0; i < 15; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"\t 处理业务");
});
}
}
private static void ThreadPoolInit() {
ExecutorService threadPool = Executors.newCachedThreadPool(); //一池n个处理线程
for (int i = 0; i < 15; i++) {
threadPool.execute(()->{
Thread.sleep(5);
System.out.println(Thread.currentThread().getName()+"\t 处理业务");
});
}
}
结果:
3.newSingleThreadPool
适合:多个任务排队执行
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
特点:
- 创建一个单一的线程池,他只会用唯一的工作线程来执行任务。
- corePoolSize = maximumPoolSize = 1,
- 生存时间为0s
- 阻塞队列是:LinkedBlockingQueue
- 阻塞队列是无界的,可以放任意数量的任务。
实例代码:
ExecutorService threadPool1 = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"\t 处理业务");
});
}
结果:
4.newScheduledThreadPool
适合:周期性执行任务的场景,需要限制线程数量的场景。
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
特点:
- 最大线程数为Integer.MAX_VALUE
- 阻塞队列是DelayedWorkQueue
- 生存时间为0s
- scheduleAtFixedRate() :按某种速率周期执行
- scheduleWithFixedDelay():在某个延迟后执行
实例代码:
线程初次执行的延迟时间为1s,后每3s进行一次线程执行。
private static void ThreadPoolInit() {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
System.out.println("start-- Time" + System.currentTimeMillis());
scheduledExecutorService.scheduleWithFixedDelay(()->{
System.out.println("current Time" + System.currentTimeMillis());
System.out.println(Thread.currentThread().getName()+"正在执行");
}, 1, 3, TimeUnit.SECONDS);
}
结果:
5.newWorkStealingPool
具有抢占式操作的线程池,每个线程都有一个任务队列存放任务。底层用的ForkJoinPool 来实现的,任务按照工作线程均分,先工作完的线程去帮助没处理完的线程工作。达到最快完成工作的现象。适合使用在很耗时的任务中。
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
特点:
- 可以传入线程的数量,不传入,则默认使用当前计算机中可用的cpu数量