栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Spring线程池和定时任务

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Spring线程池和定时任务

概述

Spring框架提供了线程池和定时任务执行的接口:TaskExecutor和TaskScheduler 来支持异步执行任务和定时执行任务功能。
同时使用框架自己定义的抽象接口来屏蔽掉底层JDK版本间以及Java EE中的线程池和定时任务处理的差异。
另外Spring还支持集成JDK内部的定时器Timer和第三方提供的定时器Quartz Scheduler框架。

线程池接口:TaskExecutor

public interface TaskExecutor extends Executor
在Spring的一些其他组件中比如ApplicationEventMulticaster,Quartz都是使用TaskExecutor来作为线程池的抽象的。
常用三个实现类:SimpleAsyncTaskExecutor、SimpleThreadPoolTaskExecutor、ThreadPoolTaskExecutor

SimpleAsyncTaskExecutor

此实现支持任务的异步执行,但是此实现没有线程的复用,每次执行一个提交的任务时候都会新建一个线程,任务执行完成后会将线程关闭,最大并发数默认是没有限制的,但是可以通过方法 setConcurrencyLimit(int concurrencyLimit)来设置最大并发数。
一般使用线程池来代替此实现,特别是执行一些生命周期很短的任务的时候。

SimpleThreadPoolTaskExecutor

当我们有Quartz和非Quartz共享同一个线程池的需求的时候使用SimpleThreadPoolTaskExecutor。

ThreadPoolTaskExecutor

我们一般是使用 ThreadPoolTaskExecutor,此实现可以通过属性注入来配置线程池的相关配置。

Demo

定义一个任务DataSimulation:

package TaskExecutorDemo;
import org.springframework.stereotype.Component;
import java.util.Random;

@Component
public class DataSimulation implements Runnable {
    @Override
    public void run() {
        Random random = new Random();
        System.out.println("[" + Thread.currentThread().getName() + "]" +"-" + random.nextInt(10));
    }
}

定一个类,注入TaskExecutor:

package TaskExecutorDemo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;

@Component
public class DataFacotory {
    @Autowired
    public TaskExecutor executor;

    public TaskExecutor getExecutor() {
        return executor;
    }

    public void setExecutor(TaskExecutor executor) {
        this.executor = executor;
    }

    public void dataFactory(){

        for (int i =0; i < 10; i++){
            executor.execute(new DataSimulation());
        }
    }
}

applicationContext.xml中定义bean id = “taskExecutor”,并配置ThreadPoolTaskExecutor的属性:




    
    
        
        
        
    

调用:

public class testTaskExecutor {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"TaskExecutorDemo/applicationContext.xml"});

        DataFacotory df = (DataFacotory)context.getBean("dataFacotory");
        df.dataFactory();
    }
}

运行结果:

完成配置后即可使用此线程池,使用类通过 @Autowired public TaskExecutor executor 注入配置的线程池(id = “taskExecutor”),即可使用。
Spring提供的线程池可以通过配置文件配置线程池的配置,相比JDK自带的线程池是一个很大的优势。

好处

通过使用线程池来实现线程的复用,减少线程创建和销毁的开销。将执行线程的任务交给线程池来操作,一定意义上实现了解耦。使用线程池可以控制任务的最大并发数目,这个在防止内存溢出以及并发优化方面有很重要的作用。 定时任务接口:TaskScheduler

public interface TaskScheduler
接口方法:

ScheduledFuture schedule(Runnable task, Trigger trigger):通过触发器来决定task是否执行ScheduledFuture schedule(Runnable task, Date startTime):在starttime的时候执行一次ScheduledFuture scheduleAtFixedRate(Runnable task, long period):每隔period执行一次ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period):从starttime开始每个period时间段执行一次taskScheduledFuture scheduleWithFixedDelay(Runnable task, long delay;:每隔delay时间执行一次ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay):从startTime开始每隔delay长时间执行一次 Trigger

TaskScheduler中将会使用到Trigger对象,Trigger接口用于计算任务的下次执行触发时间。通过实现Trigger接口可以实现自定义触发器来执行执行task。
当然Spring也提供了两个默认的实现类:PeriodicTrigger和CronTrigger。

CronTrigger

通过Cron表达式来生成调度计划。比如:scheduler.schedule(task, new CronTrigger(“0 15 9-17 * * MON-FRI”));
cron表达式含义见《cron表达式》

Spring对cron表达式的支持,是由CronSequenceGenerator来实现的,不依赖于别的框架。下面给出一个Demo感受下:

public static void main(String[] args) {
    CronSequenceGenerator generator = new CronSequenceGenerator("0 15 * * * MON-FRI");

    Date next = generator.next(new Date());
    System.out.println(next); //Mon Apr 22 17:15:00 CST 2021
    System.out.println(generator.next(next)); //Mon Apr 22 18:15:00 CST 2021

}
PeriodicTrigger

用于定期执行的Trigger。它有两种模式:

fixedRate:两次任务开始时间之间间隔指定时长fixedDelay: 上一次任务的结束时间与下一次任务开始时间``间隔指定时长

默认情况下PeriodicTrigger使用了fixedDelay模式。

TaskScheduler

Spring任务调度器的核心接口,定义了执行定时任务的主要方法,主要根据任务的不同触发方式调用不同的执行逻辑,其实现类都是对JDK原生的定时器或线程池组件进行包装,并扩展额外的功能。
它有如下实现类:

ThreadPoolTaskScheduler

public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
    implements AsyncListenableTaskExecutor, SchedulingTaskExecutor, TaskScheduler
除实现了TaskScheduler接口中的方法外,它还包含了一些对ScheduledThreadPoolExecutor进行操作的接口,大多数场景下都使用它来进行任务调度。

Demo
package TaskSchedulerDemo;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;

public class testTaskExecutor {
    public static void main(String[] args) {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(6);
        taskScheduler.initialize(); // 务必调用此方法来启动

        // 执行任务
        // 执行一次
        taskScheduler.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "  我只会被执行一次~~~");
            }
        });
        //lambda表达式,多用于匿名内部类、forEach()方法等。小括号()用于传参,大括号{}用于执行相关操作、返回值等。
        //taskScheduler.execute(() -> System.out.println(Thread.currentThread().getName() + "  我只会被执行一次~~~"));
        // 周期性执行
        taskScheduler.schedule(() -> System.out.println(Thread.currentThread().getName() + " 我会被多次执行~~~"), new CronTrigger("0/2 * * * * ?"));

        // 此处:若你有周期性的任务,这里不要shutdown()
        //taskScheduler.shutdown();
    }
}

执行结果:

注:使用前必须得先调用initialize()【初始化方法】。shutDown()方法执行完后可以关闭线程。

ConcurrentTaskScheduler

以单个线程方式执行定时任务,适用于简单场景。

public class testTaskExecutor {
    public static void main(String[] args) {
        ConcurrentTaskScheduler taskScheduler = new ConcurrentTaskScheduler();

        // 执行任务
        // 执行一次
        taskScheduler.execute(() -> System.out.println(Thread.currentThread().getName() + "  我只会被执行一次~~~"));
        // 周期性执行
        taskScheduler.schedule(() -> System.out.println(Thread.currentThread().getName() + " 我会被多次执行~~~"), new CronTrigger("0/2 * * * * ?"));

        // 此处:若你有周期性的任务,这里不要shutdown()
        //taskScheduler.shutdown();
    }
}

执行结果:

执行的线程都是一样的。

注入方式

applicationContext.xml配置文件配置ThreadPoolTaskScheduler:



    

SchedulerFacotory类注入TaskScheduler 对象:

@Component
public class SchedulerFacotory {
    @Autowired
    public TaskScheduler scheduler;

    public TaskScheduler getScheduler() {
        return scheduler;
    }

    public void setScheduler(TaskScheduler scheduler) {
        this.scheduler = scheduler;
    }

    public void schedulerFactory(){
        scheduler.schedule(new DataSimulation(),new CronTrigger("0/2 * * * * ?"));
    }
}

调用:

public class testTaskExecutor {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"TaskSchedulerDemo/applicationContext.xml"});

        SchedulerFacotory sf = (SchedulerFacotory)context.getBean("schedulerFacotory");
        sf.schedulerFactory();
    }
}

运行结果:

注解方式

1、Spring配置文件中启用注解配置如下:





2、创建SchedulerPoolService,并在service中使用 @Scheduled 注解创建定时任务

package TaskSchedulerDemo;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

@Service
public class SchedulerPoolService {

    @Scheduled(cron = "0/3 * * * * ?")
    public void task1(){
        Thread thread =  Thread.currentThread();
        System.out.println("[" + Thread.currentThread().getName() + "]" + "task1-id:" + thread.getId() + ",group:" + thread.getThreadGroup());
    }

    @Scheduled(fixedDelay = 5000)
    public void task2(){
        Thread thread =  Thread.currentThread();
        System.out.println("[" + Thread.currentThread().getName() + "]"+ "task2-id:" + thread.getId() + ",group:" + thread.getThreadGroup());
    }
}

3、加载配置文件即可

public class testTaskExecutor {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"TaskSchedulerDemo/applicationContext.xml"});
    }
}

运行结果:

还可以通过配置类,@EnableScheduling 开启配置计划任务

@Configuration
@ComponentScan("TaskSchedulerDemo")
@EnableScheduling // 开启配置计划任务
public class SpringConfig {
}
@Async异步注解

Spring默认的事件机制是同步的。举个例子:

@Service
public class SchedulerPoolService {
    @Scheduled(fixedDelay = 3000)
    public void task(){
        Thread thread =  Thread.currentThread();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("[" + Thread.currentThread().getName() + "]"+ "[" + new Date() + "]"+"task-id:" + thread.getId() + ",group:" + thread.getThreadGroup());
    }
}

在原先service增加触发时间打印,而且sleep(5000)。
运行结果:

可以看出,任务每8秒执行一次,是轮询秒数(3秒)+ 单次任务执行时间(5秒),说明任务是同步执行。

但有时候需要任务异步执行,不然太耗时,Spring提供 @Async注解标注异步执行。

@Service
public class SchedulerPoolService {
    //此注解为异步方法注解,如果注解到类上,表示此类的所有方法都为异步方法
    @Async()
    @Scheduled(fixedDelay = 3000)
    public void task(){
        Thread thread =  Thread.currentThread();
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("[" + Thread.currentThread().getName() + "]"+ "[" + new Date() + "]"+"task-id:" + thread.getId() + ",group:" + thread.getThreadGroup());
    }
}

运行结果:

可以看出,任务每3秒执行一次,而且线程号也不一样,说明是异步执行。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/703671.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号