java 线程池 spring线程池 多线程知识总结

Posted QQ_851228082

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java 线程池 spring线程池 多线程知识总结相关的知识,希望对你有一定的参考价值。

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中线程池分为两种,一种是普通线程池,一种是定时任务线程池,他们分别是ThreadExecutorThreadScheduling接口。在配置类上添加@EnableScheduling,在方法上添加@Schedule,启用定时任务。在配置类上添加@EnableAsync,在方法上添加@Async,启用异步方法。

异步方法

@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.ThreadPoolExecutororg.springframework.scheduling.concurrent.ThreadPoolTaskExecutor,异步方法也是使用这个线程池。一种是调度线程池,用来执行定时任务,名字一般是xxxScheduler比如org.springframework.scheduling.concurrent.ThreadPoolTaskSchedulerjava.util.concurrent.ScheduledThreadPoolExecutor,spring中

  • @Async设置异步方法,@EnableAsync启用异步方法,默认使用executor线程池。
  • @EnableScheduling启用定时任务,@Scheduled设置定时任务执行时机,两种触发方法,固定延迟触发fixedDelay,固定速率触发fixedRate,

默认的,这两种线程池都是单线程线城池(核心线程数1,最大线程数不限,队列容量不限)

以上是关于java 线程池 spring线程池 多线程知识总结的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot中异步线程池@Async详解

分享知识-快乐自己:Spring线程池配置

java面试总躲不过的并发: 线程池ThreadPoolExecutor基础梳理

java核心知识点 --- 线程池ThreadPool

带你整理面试过程中关于多线程中的线程池的相关知识点

spring异步线程任务Async,自定义配置线程池,Java