SpringBoot多線程定時調度@Scheduled

 

@Scheduled在spring中默認是使用一個線程的線程池執行調度任務的。

下面是我的測試代碼:

1、pom文件配置

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.wsj.monitor</groupId>
    <artifactId>monitor</artifactId>
    <version>1.0-SNAPSHOT</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.7.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-quartz</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
       
    </dependencies>


</project>

 啓動類配置:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class MonitorApp {
    public static void main(String[] args) {
        SpringApplication.run(MonitorApp.class,args);
    }
}

 測試job

package com.tyyd.monitor.job;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

@Component
public class TestJob {

    @Scheduled(cron = "0/10 * * * * ?")
    public void testJob1() throws InterruptedException {
        System.out.println("執行了TestJob1,執行的線程名稱是:"+Thread.currentThread().getName());
        //休眠20秒
        TimeUnit.SECONDS.sleep(20);
    }

    @Scheduled(cron = "0/5 * * * * ?")
    public void testJob2(){
        System.out.println("執行了TestJob2,執行的線程名稱是:"+Thread.currentThread().getName());
    }
}

執行結果,看到使用的都是同線程池的線程。 

執行了TestJob2,執行的線程名稱是:pool-1-thread-1
執行了TestJob2,執行的線程名稱是:pool-1-thread-1
執行了TestJob1,執行的線程名稱是:pool-1-thread-1
執行了TestJob2,執行的線程名稱是:pool-1-thread-1
執行了TestJob2,執行的線程名稱是:pool-1-thread-1
執行了TestJob1,執行的線程名稱是:pool-1-thread-1

線程池使用配置:

package com.wsj.monitor.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;

import java.util.concurrent.Executors;

/***
 * @Scheduled 註解默認使用的是單線程,1個任務卡死。會產生連鎖反應。
 * 配置任務線程池可以讓同步任務並行執行調度
 * @Scheduled(cron="0 1***?")
 */
@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        //設定一個啓動10個線程的定時調度線程池
        taskRegistrar.setScheduler(Executors.newScheduledThreadPool(10));
    }
}

配置後執行結果

執行了TestJob2,執行的線程名稱是:pool-1-thread-1
執行了TestJob2,執行的線程名稱是:pool-1-thread-2
執行了TestJob1,執行的線程名稱是:pool-1-thread-1
執行了TestJob2,執行的線程名稱是:pool-1-thread-3
執行了TestJob2,執行的線程名稱是:pool-1-thread-2
執行了TestJob2,執行的線程名稱是:pool-1-thread-4
執行了TestJob2,執行的線程名稱是:pool-1-thread-3
執行了TestJob2,執行的線程名稱是:pool-1-thread-5
執行了TestJob2,執行的線程名稱是:pool-1-thread-6
執行了TestJob1,執行的線程名稱是:pool-1-thread-2
執行了TestJob2,執行的線程名稱是:pool-1-thread-4
執行了TestJob2,執行的線程名稱是:pool-1-thread-4
執行了TestJob2,執行的線程名稱是:pool-1-thread-3
執行了TestJob2,執行的線程名稱是:pool-1-thread-3
執行了TestJob2,執行的線程名稱是:pool-1-thread-3

注意,spring 的quartz是可以使用異步的。再啓動類上開啓異步支持@EnableAsync 並在要異步的任務上加個@Async。

如果所有的任務都使用異步,線程池執行的話,可以不配置同步任務線程池。但是使用異步需要注意的是異步線程池的大小配置。異步的線程池默認是無上限的開啓線程數的。

@Component
public class TestJob {

    @Async
    @Scheduled(cron = "0/3 * * * * ?")
    public void testJob1() throws InterruptedException {
        System.out.println("執行了TestJob1,執行的線程名稱是:"+Thread.currentThread().getName());
        TimeUnit.SECONDS.sleep(20);
    }

    @Scheduled(cron = "0/5 * * * * ?")
    public void testJob2(){
        System.out.println("執行了TestJob2,執行的線程名稱是:"+Thread.currentThread().getName());
    }
}

執行結果: 

執行了TestJob1,執行的線程名稱是:SimpleAsyncTaskExecutor-1
執行了TestJob1,執行的線程名稱是:SimpleAsyncTaskExecutor-2
執行了TestJob2,執行的線程名稱是:pool-1-thread-1
執行了TestJob1,執行的線程名稱是:SimpleAsyncTaskExecutor-3
執行了TestJob2,執行的線程名稱是:pool-1-thread-2
執行了TestJob1,執行的線程名稱是:SimpleAsyncTaskExecutor-4
執行了TestJob1,執行的線程名稱是:SimpleAsyncTaskExecutor-5
執行了TestJob2,執行的線程名稱是:pool-1-thread-3
執行了TestJob1,執行的線程名稱是:SimpleAsyncTaskExecutor-6
執行了TestJob1,執行的線程名稱是:SimpleAsyncTaskExecutor-7
執行了TestJob2,執行的線程名稱是:pool-1-thread-2
執行了TestJob1,執行的線程名稱是:SimpleAsyncTaskExecutor-8
執行了TestJob2,執行的線程名稱是:pool-1-thread-8
執行了TestJob1,執行的線程名稱是:SimpleAsyncTaskExecutor-9
執行了TestJob1,執行的線程名稱是:SimpleAsyncTaskExecutor-10

 因爲異步線程池沒做控制默認無上限肯定不行的。修改配置如下:

package com.wsj.monitor.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;

import java.util.concurrent.Executors;

/***
 * @Scheduled 註解默認使用的是單線程,1個任務卡死。會產生連鎖反應。
 * 配置任務線程池可以並行執行調度
 * @Async
 * @Scheduled(cron="0 1***?")
 * 可以在scheduled上加上異步註解 ,每個任務都是一個新的線程
 * 如果是異步,每卡住一個任務都會佔用一個線程資源,直到線程池線程資源全部佔用定時調度掛掉
 */
@Configuration
@EnableAsync
public class ScheduleConfig implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        //設定一個長度10的定時任務線程池
        taskRegistrar.setScheduler(Executors.newScheduledThreadPool(10));
    }

    /**
     * 異步線程池設置
     * @return
     */
    @Bean
    SimpleAsyncTaskExecutor simpleAsyncTaskExecutor(){
        SimpleAsyncTaskExecutor simpleAsyncTaskExecutor = new SimpleAsyncTaskExecutor();
        simpleAsyncTaskExecutor.setConcurrencyLimit(2);//設置最大並行數
        simpleAsyncTaskExecutor.setDaemon(true); //設置爲守護線程
        return  simpleAsyncTaskExecutor;
    }
}

修改配置後,看到執行結果最大同時異步爲2個結果如下: 

執行了TestJob1,執行的線程名稱是:SimpleAsyncTaskExecutor-1
執行了TestJob2,執行的線程名稱是:pool-1-thread-2
執行了TestJob1,執行的線程名稱是:SimpleAsyncTaskExecutor-2
執行了TestJob2,執行的線程名稱是:pool-1-thread-4
執行了TestJob2,執行的線程名稱是:pool-1-thread-1
執行了TestJob2,執行的線程名稱是:pool-1-thread-5
執行了TestJob1,執行的線程名稱是:SimpleAsyncTaskExecutor-3
執行了TestJob2,執行的線程名稱是:pool-1-thread-6
執行了TestJob1,執行的線程名稱是:SimpleAsyncTaskExecutor-4
執行了TestJob2,執行的線程名稱是:pool-1-thread-1
執行了TestJob2,執行的線程名稱是:pool-1-thread-8
執行了TestJob2,執行的線程名稱是:pool-1-thread-8
執行了TestJob1,執行的線程名稱是:SimpleAsyncTaskExecutor-5
執行了TestJob2,執行的線程名稱是:pool-1-thread-8
執行了TestJob1,執行的線程名稱是:SimpleAsyncTaskExecutor-6
執行了TestJob2,執行的線程名稱是:pool-1-thread-8
執行了TestJob2,執行的線程名稱是:pool-1-thread-5
執行了TestJob2,執行的線程名稱是:pool-1-thread-5

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章