jdk线程池
线程池的几个重要参数
线程池有有界/无界队列、拒绝策略、核心线程数、最大线程数、空闲时间这几个重要参数,线程池的执行流程是,来了一个任务,如果核心线程数未满,则创建线程执行;如果核心线程数已满,则将任务放到任务队列中,如果任务队列满了,如果达到最大线程数,则执行拒绝策略,如果未达到最大线程数,则创建线程执行任务。超过核心线程数的线程,会根据是否达到空闲时间进行销毁。
核心线程数
核心线程数,就是永不销毁的线程数。
最大线程数
如果最大线程数超过核心线程数,则多余的线程会在超过空闲时间参数时,销毁。
队列
队列分为有界队列和无界队列,无界队列就是容量无限,存在内存溢出风险。
拒绝策略
如果队列满了,则执行拒绝策略,拒绝策略有直接抛出异常、忽略、由调用方执行。默认的拒绝策略是抛出异常。
常见的3种线程池
Executors是Executor的工具类,可以用来创建3种线程池,线程池的实现类是ThreadPoolExecutor
,常见的有3种线程池,它们都是根据ThreadPoolExecutor
的参数不同创建出来的。
- newFixedThreadPool 核心线程数和最大线程数相同,线程不会过期,队列是 LinkedBlockingQueue,容量是Inteter.max_value, 无界队列
- newCachedThreadPool 核心线程数是0,最大线程数不限,60s过期,SynchronousQueue 队列容量为0,所以如果提交速度大于任务处理速度,线程会一直增加。
- singleThreadPool 核心线程数和最大线程数都是1,线程不会过期,相当于特殊的fixedThreadPool,使用了LinkedBlockingQueue 无界队列
submit vs execute
ExecutorService的submit和execute方法区别
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
void execute(Runnable command);
从方法签名可以看出,submit有返回值Future,并且参数可是Runnable、Callable,execute无返回值,参数值只能是Runnable。这里你肯定好奇,Runnable的run方法没有返回值,那么submit(Runnable task)的返回值从哪里来的,看如下代码,将Runnable r包装为FutureTask时,结果值设置为null,当然也可以指定值。
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
怎么执行Callable?
因为Thread(Runnable r)构造方法只接收Runnable,所以先将Callable转换为FutureTask(实现了Runnable),然后再执行
Future<T> ExecutorService.submit(Callable<T> c)
Future,代表未来,也就是计算结果不能立刻得到FutureTask,顾名思义,实现了Runnable和Future两个接口,所以既是一个线程,也是一个future
ExecutorService创建自定义线程池
从上述3种常见线程池,可以得知各有优缺点,如果想自定义线程池,那么就得使用java.util.concurrent.ThreadPoolExecutor
,上边3种常见线程池也是用的ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
ThreadPoolExecutor的shutdown vs shutdownnow方法
shutdown,不接收新任务,处理已提交任务,然后关闭。
shutdownnow,不接收新任务,取消待执行任务,终止正在运行任务,但不保证正在执行任务能被中断,取决于task实现。
定时任务线程池
除了这3种线程池,还有调度线程池java.util.concurrent.ScheduledThreadPoolExecutor
,对应定时任务场景。
scheduleAtFixedRate
方法,以固定速率执行任务
scheduleWithFixedDelay
方法,延迟指定时间后执行
Spring 线程池
spring中线程池分为两种,一种是普通线程池,一种是定时任务线程池,他们分别是ThreadExecutor
、ThreadScheduling
[email protected],[email protected],[email protected],[email protected],启用异步方法。
异步方法
@Async,表示方法是异步的,调用方法会立刻返回,它用的哪个线程池?用的 executor线程池,默认的executor线程池是单线程池,但同jdk的单线程池不太一样,它的核心线程数是1,最大线程数不限,队列容量不限。也可以自定义线程池
配置文件方式
<task:annotation-driven executor="myExecutor" />
<task:executor id="myExecutor" pool-size="5"/>
javabean方式
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setPoolSize(10);// 自定义线程个数
return taskExecutor;
}
定时任务
@Scheduled,表示定时任务,这个注解有delay、rate、cron属性,delay表示延迟,上次任务执行完成后,delay某个时间段后,再执行下一个任务。rate是固定速率执行,不care上一个任务执行状态,比如每2分钟执行。cron是cron表达式,是最灵活、最强大的执行时机表示。默认的scheduled线程池,核心线程数1,最大线程数不限,队列容量不限。我们也可以自定义schedule线程池
配置文件方式
<task:annotation-driven scheduler="myScheduler"/>
<task:scheduler id="myScheduler" pool-size="10"/>
javabean方式
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(10);// 自定义线程个数
return taskScheduler;
}
@Scheduled注解
下边的例子表示,上个任务执行完成,5秒后,再执行
@Scheduled(fixedDelay=5000)
public void doSomething() {
// something that should run periodically
}
下边的例子表示,不管上个任务是否执行完毕,每隔5秒执行一次
@Scheduled(fixedRate=5000)
public void doSomething() {
// something that should run periodically
}
下边的例子表示,第一次执行时延迟1秒再执行,后边每隔5秒执行一次
@Scheduled(initialDelay=1000, fixedRate=5000)
public void doSomething() {
// something that should run periodically
}
Monday-Firday,每5秒执行一次
@Scheduled(cron="*/5 * * * * MON-FRI")
public void doSomething() {
// something that should run on weekdays only
}
总结
jdk和spring分别有两种线程池,一种是普通线程池,比如发送http请求,但不想影响主线程执行,那么就是用这个线程池名字一般是xxxExecutor比如java.util.concurrent.ThreadPoolExecutor
、org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
,异步方法也是使用这个线程池。一种是调度线程池,用来执行定时任务,名字一般是xxxScheduler比如org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler
、java.util.concurrent.ScheduledThreadPoolExecutor
,spring中
- @Async设置异步方法,@EnableAsync启用异步方法,默认使用executor线程池。
- @EnableScheduling启用定时任务,@Scheduled设置定时任务执行时机,两种触发方法,固定延迟触发fixedDelay,固定速率触发fixedRate,
默认的,这两种线程池都是单线程线城池(核心线程数1,最大线程数不限,队列容量不限)
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/20251.html