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

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

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

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/20251.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论