文章目录
- 一、ThreadPoolExecutor
- 二、ScheduledExecutorService
- 1.1 public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)
- 1.2 public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit);
- 1.3 public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period,TimeUnit unit);
- 1.4 public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay, TimeUnit unit);
- 三、ScheduledThreadPoolExecutor
一、ThreadPoolExecutor
1.1 常用参数
corePoolSize:核心线程数
maximumPoolSize:线程池最大允许线程数
workQueue:任务队列
threadFactory:线程创建工厂
handler: 任务拒绝策
keepAliveTime, unit:等待时长
1.2 执行流程
- 首先判断线程数是否大于corePoolSize
- 否,threadFactory创建新线程执行任务
- 是,判断线程数是否大于maximumPoolSize?
- 是,handler执行拒绝策略
- 否,检查队列是否已经满了
- 否,放入队列中,等待线程执行
- 是,创建新的队列,放到新的队列中
注意:
当线程数超过corePoolSize,并且超过的线程处于空闲状态,则超过Alivetime,空闲线程就会被中资。
1.3 workQueue的实现
- SynchronouseQueue
这是一个空队列,不会保存提交的task - ArrayBlockingQueue
数组实现的队列,可以指定数组的长度 - LinkedBlockingQueue
数组实现的队列,也可以指定队列的长度
1.4 RejectedExecutionHandler的实现
- AbortPolicy
直接抛出RejectedExecutionException,这是线程池中的默认实现 - DiscardPolicy
什么都不做 - DiscardOldestPolicy
丢弃workQueue队头任务,加入新任务 - CallerRunsPolicy
直接在调用者的线程执行任务
1.5 Execute方法
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
- 判断Runnable是否为空,为空,抛出空指针异常
- 获取线程池中线程数量,如果小于corePoolSize,则添加新线程执行任务,添加成功返回
- 添加失败,则重新获取线程数,如果线程数大于corePoolSize,则将任务添加到任务队列中(前提是线程池未关闭)。其他,跳转到6.
4.判断线程池是否关闭,如果线程池关闭,则移除任务,并执行拒绝策略。- 如果没关闭,意味着检查通过,则新建线程去执行
6.尝试着再将任务添加到任务队列,失败,执行拒绝策略。
1.6 线程池实战
package com.example.seckilldemo.Test;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.SneakyThrows;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolTest {
public static void main(String[] args) {
//直接丢弃
new ThreadPoolExecutor.DiscardPolicy();
//拒绝策略
new ThreadPoolExecutor.AbortPolicy();
//触发拒绝策略,线程池未关闭,丢弃任务队列最久的,即队列头的任务,将任务加入队列
new ThreadPoolExecutor.DiscardOldestPolicy();
//触发拒绝策略,线程池未关闭,使用调用者线程执行任务
new ThreadPoolExecutor.CallerRunsPolicy();
LinkedBlockingDeque<Runnable> work = new LinkedBlockingDeque<>(5);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 5, 5, TimeUnit.SECONDS, work, new ThreadFactoryBuilder()
.setNameFormat("thread-自定义线程名-runner-%d").build(),new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 13; i++) {
threadPoolExecutor.submit(new Runnable() {
@SneakyThrows
@Override
public void run() {
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName()+"finish");
}
});
}
threadPoolExecutor.shutdown();
}
}
二、ScheduledExecutorService
1.1 public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)
用于定时执行任务,延迟的时间为delay*unit,它返回一个ScheduledFuture对象用于获取执行结果或者剩余延时,调用Future.get()方法将阻塞当前线程最后返回null。
1.2 public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit);
同上,不同的是,调用Future.get()方法将返回执行的结果,而不是null。
1.3 public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period,TimeUnit unit);
重复执行任务command,第一次执行时间为initialDelay延迟后,以后的执行时间将在initialDelay + period * n,unit代表时间单位,值得注意的是,如果某次执行出现异常,后面该任务就不会再执行。或者通过返回对象Future手动取消,后面也将不再执行。
1.4 public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay, TimeUnit unit);
效果同上,不同点:如果command耗时为 y,则上面的计算公式为initialDelay + period * n + y,也就是说,它的定时时间会加上任务耗时,而上面的方法则是一个固定的频率,不会算上任务执行时间!
三、ScheduledThreadPoolExecutor
3.1 继承关系
ScheduledThreadPoolExecutor继承自ThreadPoolExecutor类,实现了ScheduledExecutorService接口,上面均已经分析。
3.2 特点
最大线程数为最大值,任务队列是默认的。
四、线程池
4.1 优点
- 减少创建和销毁线程带来的时间,资源消耗,而且创建好的线程可以重复利用
- 减少因创建线程带来的时间等待,可以直接使用线程
- 可以整体的管理线程
4.2 执行流程
- 判断当前核心线程池的线程是不是都在执行任务,不是,则新建工作线程去执行任务(需要获得全局锁)
- 是,则判断任务队列是不是满了,不是,将任务添加进任务队列
- 是,判断线程池中的线程是不是已经满了,不是,则新建线程去处理(获得全局锁)
- 是,交给饱和策略去处理
4.3 工作线程
线程池创建的时候,会将线程封装成工作线程,工作线程执行完自己的任务后,会反复从线程队列中取出任务来执行。
4.4 参数
4.4.1 corePoolSize
核心线程数,调用prestartAllCoreThreads()方法,线程池会提前创建并启动所有的基本线程。
4.4.2 runnableTaskQueue(任务队列)
- ArryaBlockingQueue
基于数组的有界队列,按照先进先出原则对与元素进行排序。 - LinkedBlockingQueue
一个基于链表结构的阻塞队列,按照FIFO排序元素 - SynchronouseQueue
一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态。 - PriorityQueue
具有有优先级的无线阻塞队列
4.4.3 maximumPoolSize
线程池最大容量,如果使用无界的队列就没有什么效果了
4.4.4 ThreadFactory
设置创建线程的工厂,通过工厂给线程进行命名。
4.4.5 ExecutionHandler
- AbortPolicy
直接抛出异常 - CallerRunsPolicy
只用调用者所在线程来运行任务 - DiscardOldestPolicy
丢弃队列里最近的一个任务,并执行当前任务 - DiscardPolicu
不处理,丢弃掉
4.4.6 keepAliveTime
工作线程空闲后,保存存活的时间。
4.4.7 TimeUnit
线程活动保持时间的单位
4.5 向线程池提交任务
- Execute()
用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功 - Submit()
submit用于提交需要返回值的任务,线程池需要返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get方法获取返回值。get方法会阻塞当前线程直到任务完成。
4.6 关闭线程池
逐个遍历线程池中的工作线程,然后逐个调用线程的interrupt方法来中断线程,所以无法响应中断的线程永远不会停止。shutdownNow首先将线程池的状态设置成stop,然后尝试去关闭所有的线程,shutdown将线程的状态设置成SHUTDOWN。然后中断没有正在执行任务的线程。
只要调用了其中之一的方法,isShutdown方法就会返回true,当所有任务都关闭后,才表示线程池关闭成功,这时调用isTerminaed方法会返回true。
任务不一定执行完,则调用shutdownNow方法。
4.7 合理的配置线程池
4.7.1 首先分析任务的性质
任务的性质:cpu密集型任务,io密集型任务和混合型任务。
任务的优先级:高中低
任务的执行时间:长中段
任务的依赖性:是否依赖其他资源
4.8 线程池的监控
taskCount:线程池需要执行的任务数量
completedTaskCount:线程池在运行过程中已经完成的线程数量
largestPoolSize:线程池曾创建的最大线程的数量
getPoolSize:线程池的线程数量
getActiveCoutn:获取活动的线程数
五、任务队列
5.1 ArrayBlockingQueue
是一个基于数组结构的有界队列,此队列按FIFO(先进先出)原则对元素进行排序
5.2 LinkedBlockingQueue
一个基于链表的无界队列,此队列按照FIFO排序元素,吞吐量通常要高于ArrayBlockingQueue,静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
5.3 SynchronousQueue
一个不存储元素的有界队列,每个插入操作必须等到另一个线程的移除操作,否则插入操作一直处于阻塞状态。吞吐量高于LinkedlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
5.4 PriorityBlockingQueue
一个具有优先级的无限阻塞队列。