Java web项目创建笔记19 之《spring定时任务功能(1)》

spring整合定时任务功能
一、准备工作
1、建立maven子模块:webapp2_schedule,过程省略
2、在webapp2_schedule模块的src/main/java下,建立com.study.schedule包

二、spring自带的task功能
1、涉及的包
org.springframework.core.task
org.springframework.core.task.support

2、TaskExecutor接口
继承自jdk的Executor(spring自己封装了一层),定义了一个接收Runnable接口对象的方法execute
1)增加MyTask1.java

package com.study.schedule;

import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;

@Component
public class MyTask1 implements TaskExecutor{

	@Override
	public void execute(Runnable task) {
		// TODO Auto-generated method stub
		System.out.println("hello task.");
		task.run();
	}

}

2)增加MessagePrinterTask.java

package com.study.schedule;

public class MessagePrinterTask implements Runnable {
	private String message;

	public MessagePrinterTask(String message) {
		this.message = message;
	}

	public void run() {
		for (int i = 0; i < 25; i++) {
			System.out.println(message + " num: " +i);
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

 3)增加测试类TaskTest.java
增加测试方法testTaskExecutor

	@Test
	public void testTaskExecutor() {
		//执行实现了Runnable接口的类
		MyTask1 myTask1 = new MyTask1();
		myTask1.execute(new MessagePrinterTask("apple"));
		myTask1.execute(new MessagePrinterTask("banana"));
	}

4)测试方法执行结果

测试开始----------
hello task.
apple num: 0
apple num: 1
apple num: 2
apple num: 3
apple num: 4
apple num: 5
apple num: 6
apple num: 7
apple num: 8
apple num: 9
apple num: 10
apple num: 11
apple num: 12
apple num: 13
apple num: 14
apple num: 15
apple num: 16
apple num: 17
apple num: 18
apple num: 19
apple num: 20
apple num: 21
apple num: 22
apple num: 23
apple num: 24
hello task.
banana num: 0
banana num: 1
banana num: 2
banana num: 3
banana num: 4
banana num: 5
banana num: 6
banana num: 7
banana num: 8
banana num: 9
banana num: 10
banana num: 11
banana num: 12
banana num: 13
banana num: 14
banana num: 15
banana num: 16
banana num: 17
banana num: 18
banana num: 19
banana num: 20
banana num: 21
banana num: 22
banana num: 23
banana num: 24
测试结束----------

可以看到线程是同步执行的

3、TaskExecutor接口实现类
1)SimpleAsyncTaskExecutor
SimpleAsyncTaskExecutor是TaskExecutor的一个实现类,为每个任务触发一个新线程,异步执行它
线程不会重用,每次调用时都会重新启动一个新的线程。但它有一个最大同时执行的线程数的限制
1-1)增加MyTask2.java

package com.study.schedule;

import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.stereotype.Component;

@Component
public class MyTask2 extends SimpleAsyncTaskExecutor {

	private static final long serialVersionUID = -8958545057173751951L;

	@Override
	public void execute(Runnable task) {
		// TODO Auto-generated method stub
		System.out.println("hello task.");
		task.run();
	}
}

1-2)增加测试方法testSimpleAsyncTaskExecutor

	@Test
	public void testSimpleAsyncTaskExecutor() {
		//异步线程执行
		SimpleAsyncTaskExecutor myTask2 = new SimpleAsyncTaskExecutor();
		myTask2.execute(new MessagePrinterTask("apple"));
		myTask2.execute(new MessagePrinterTask("banana"));
	}

1-3)测试方法执行结果

测试开始----------
测试结束----------
apple num: 0
banana num: 0
apple num: 1
banana num: 1
banana num: 2
apple num: 2
apple num: 3
banana num: 3
apple num: 4
banana num: 4
apple num: 5
banana num: 5
apple num: 6
banana num: 6
apple num: 7
banana num: 7
apple num: 8
banana num: 8
apple num: 9
banana num: 9
apple num: 10
banana num: 10
banana num: 11
apple num: 11
apple num: 12
banana num: 12
apple num: 13
banana num: 13
banana num: 14
apple num: 14
apple num: 15
banana num: 15
apple num: 16
banana num: 16
apple num: 17
banana num: 17
apple num: 18
banana num: 18
banana num: 19
apple num: 19
apple num: 20
banana num: 20
apple num: 21
banana num: 21
apple num: 22
banana num: 22
apple num: 23
banana num: 23
banana num: 24
apple num: 24

可以看到主线程执行完后,任务线程是异步执行的

2)SyncTaskExecutor
和自己实现TaskExecutor接口一样,同步执行
2-1)增加测试方法testSyncTaskExecutor

	@Test
	public void testSyncTaskExecutor() {
		//同步线程执行
		//和自己实现TaskExecutor接口一样
		SyncTaskExecutor myTask3 = new SyncTaskExecutor();
		myTask3.execute(new MessagePrinterTask("apple"));
		myTask3.execute(new MessagePrinterTask("banana"));
	}

2-2)测试方法执行结果

测试开始----------
apple num: 0
apple num: 1
apple num: 2
apple num: 3
apple num: 4
apple num: 5
apple num: 6
apple num: 7
apple num: 8
apple num: 9
apple num: 10
apple num: 11
apple num: 12
apple num: 13
apple num: 14
apple num: 15
apple num: 16
apple num: 17
apple num: 18
apple num: 19
apple num: 20
apple num: 21
apple num: 22
apple num: 23
apple num: 24
banana num: 0
banana num: 1
banana num: 2
banana num: 3
banana num: 4
banana num: 5
banana num: 6
banana num: 7
banana num: 8
banana num: 9
banana num: 10
banana num: 11
banana num: 12
banana num: 13
banana num: 14
banana num: 15
banana num: 16
banana num: 17
banana num: 18
banana num: 19
banana num: 20
banana num: 21
banana num: 22
banana num: 23
banana num: 24
测试结束----------

可以看到线程是同步执行的

4、ThreadPoolTaskExecutor
有线程池的TaskExecutor
1)在webapp2_web模块的src/main/resources下添加spring-job.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:task="http://www.springframework.org/schema/task"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
	
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/task 
        http://www.springframework.org/schema/task/spring-task.xsd
        ">
	
	<bean id="threadPoolTaskExecutor"
		class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
		<!-- 核心线程数 -->
		<property name="corePoolSize" value="5" />
		<!-- 最大线程数 -->
		<property name="maxPoolSize" value="10" />
		<!-- 队列最大长度 -->
		<property name="queueCapacity" value="25" />
	</bean>

</beans>

applicationContext.xml添加引用 

<import resource="classpath:/spring-job.xml"/>

2)添加测试方法testThreadPoolTaskExecutor并注入对象

	@Autowired
	ThreadPoolTaskExecutor threadPoolTaskExecutor;

	...

	@Test
	public void testThreadPoolTaskExecutor() {
		//带线程池的TaskExecutor
		//异步线程执行
		//可自己配置线程池大小
		threadPoolTaskExecutor.execute(new MessagePrinterTask("apple"));
		threadPoolTaskExecutor.execute(new MessagePrinterTask("banana"));
	}

3)测试方法执行结果

测试开始----------
测试结束----------
apple num: 0
banana num: 0
banana num: 1
apple num: 1
apple num: 2
banana num: 2
banana num: 3
apple num: 3
banana num: 4
apple num: 4
banana num: 5
apple num: 5
apple num: 6
banana num: 6
banana num: 7
apple num: 7
banana num: 8
apple num: 8
banana num: 9
apple num: 9
apple num: 10
banana num: 10
apple num: 11
banana num: 11
banana num: 12
apple num: 12
banana num: 13
apple num: 13
banana num: 14
apple num: 14
banana num: 15
apple num: 15
apple num: 16
banana num: 16
banana num: 17
apple num: 17
apple num: 18
banana num: 18
apple num: 19
banana num: 19
apple num: 20
banana num: 20
apple num: 21
banana num: 21
apple num: 22
banana num: 22
apple num: 23
banana num: 23
banana num: 24
apple num: 24

可以看到任务线程是异步执行的

二、spring自带scheduler功能
1、涉及的包
org.springframework.scheduling
org.springframework.scheduling.concurrent

2、TaskScheduler接口
根据不同的triggers执行任务

3、TaskScheduler接口实现类
1)ThreadPoolTaskScheduler
1-1)在spring-job.xml添加bean

	<bean id="threadPoolTaskScheduler"
		class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
		<property name="poolSize" value="5" />
	</bean>

1-2)添加测试方法testThreadPoolTaskScheduler并注入对象

	@Autowired
	ThreadPoolTaskScheduler threadPoolTaskScheduler;

	...

	@Test
	public void testThreadPoolTaskScheduler() {
		threadPoolTaskScheduler.schedule(new MessagePrinterTask("apple"), new CronTrigger("0/30 * * * * ?"));
	}

1-3)执行测试方法,发生报错

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'java.util.concurrent.ThreadFactory' available: expected single matching bean but found 2: threadPoolTaskExecutor,threadPoolTaskScheduler

4、分析问题,为什么ThreadPoolTaskExecutor和ThreadPoolTaskScheduler不能共存
1)区别
ThreadPoolTaskExecutor是一个专门用于异步执行任务的类(执行线程)
ThreadPoolTaskScheduler是一个专门用于调度执行任务的类(调度+执行线程)
2)从报错信息看
ThreadPoolTaskExecutor提供默认ThreadFactory实现
ThreadPoolTaskScheduler也提供了默认ThreadFactory实现
3)ThreadPoolTaskExecutor在哪里定义ThreadFactory?
在ThreadPoolTaskExecutor类里的initializeExecutor()方法,会初始化ThreadPoolExecutor对象,ThreadFactory由初始化的调用方ExecutorConfigurationSupport提供
4)ThreadPoolTaskScheduler在哪里定义ThreadFactory?ThreadPoolTaskScheduler的执行线程在哪里定义?
和ThreadPoolTaskExecutor初始化过程相同,初始化ScheduledThreadPoolExecutor --> (继承)ThreadPoolExecutor

ThreadPoolTaskExecutor和ThreadPoolTaskScheduler --> (继承)ExecutorConfigurationSupport --> (继承)CustomizableThreadFactory --> 实现了ThreadFactory接口
执行线程就是初始化返回的ScheduledThreadPoolExecutor线程池对象,实现了ScheduledExecutorService接口

5、重新测试ThreadPoolTaskScheduler类
1)注释掉threadPoolTaskExecutor相关的代码
2)修改testThreadPoolTaskScheduler测试方法

	@Test
	public void testThreadPoolTaskScheduler() {
		threadPoolTaskScheduler.schedule(new MessagePrinterTask("apple"), new CronTrigger("0/10 * * * * ?"));
		threadPoolTaskScheduler.execute(new MessagePrinterTask("banana"));
		try {
			System.out.println("main thread sleep start.");
			Thread.sleep(36000); //睡眠36秒
			System.out.println("main thread sleep stop.");
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

3)测试方法执行结果

测试开始----------
main thread sleep start.
banana num: 0
banana num: 1
banana num: 2
banana num: 3
banana num: 4
banana num: 5
banana num: 6
banana num: 7
banana num: 8
banana num: 9
banana num: 10
banana num: 11
banana num: 12
banana num: 13
banana num: 14
banana num: 15
banana num: 16
banana num: 17
banana num: 18
banana num: 19
banana num: 20
banana num: 21
banana num: 22
banana num: 23
banana num: 24
apple num: 0
apple num: 1
apple num: 2
apple num: 3
apple num: 4
apple num: 5
apple num: 6
apple num: 7
apple num: 8
apple num: 9
apple num: 10
apple num: 11
apple num: 12
apple num: 13
apple num: 14
apple num: 15
apple num: 16
apple num: 17
apple num: 18
apple num: 19
apple num: 20
apple num: 21
apple num: 22
apple num: 23
apple num: 24
apple num: 0
apple num: 1
apple num: 2
apple num: 3
apple num: 4
apple num: 5
apple num: 6
apple num: 7
apple num: 8
apple num: 9
apple num: 10
apple num: 11
apple num: 12
apple num: 13
apple num: 14
apple num: 15
apple num: 16
apple num: 17
apple num: 18
apple num: 19
apple num: 20
apple num: 21
apple num: 22
apple num: 23
apple num: 24
apple num: 0
apple num: 1
apple num: 2
apple num: 3
apple num: 4
apple num: 5
apple num: 6
apple num: 7
apple num: 8
apple num: 9
apple num: 10
apple num: 11
apple num: 12
apple num: 13
apple num: 14
apple num: 15
apple num: 16
apple num: 17
apple num: 18
apple num: 19
apple num: 20
apple num: 21
apple num: 22
apple num: 23
apple num: 24
main thread sleep stop.
测试结束----------
apple num: 0
apple num: 1
apple num: 2
apple num: 3
apple num: 4
apple num: 5
apple num: 6
apple num: 7
apple num: 8
apple num: 9
apple num: 10
apple num: 11
apple num: 12
apple num: 13
apple num: 14
apple num: 15
apple num: 16
apple num: 17
apple num: 18
apple num: 19
apple num: 20
apple num: 21
apple num: 22
apple num: 23
apple num: 24

可以看到任务执行都是异步的,主线程启动 --> 主线程睡眠开始 --> 打印banana任务 --> 定时打印apple任务(共触发了4次) --> 主线程睡眠结束 --> 主线程运行结束

6、增加定时任务启动类ScheduleStart.java

package com.study.schedule;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

/**
 * 初始化定时任务
 * @author User
 *
 */
@Component
public class ScheduleStart implements ApplicationListener<ContextRefreshedEvent> {

	private static final Logger logger = LoggerFactory.getLogger(ScheduleStart.class);
	
	@Autowired
	ThreadPoolTaskScheduler threadPoolTaskScheduler;
	
	@Override
	public void onApplicationEvent(ContextRefreshedEvent event) {
		if (event.getApplicationContext().getParent() == null){
            logger.info("applicationContext初始化完成...");
            this.start();
		}
	}

	public void start() {
		//在这里启动threadPoolTaskScheduler相关的定时任务
	}

}

三、小结
用到的方式有
(TaskExecutor接口是spring提供的,它继承jdk中的java.util.concurrent.Executor接口)
1、自己实现TaskExecutor接口,同步执行线程
2、spring的SimpleAsyncTaskExecutor,异步执行线程
3、spring的SyncTaskExecutor,同步执行线程
4、spring的ThreadPoolTaskExecutor,带线程池,异步执行线程
5、spring的ThreadPoolTaskScheduler,带线程池,带任务调度,异步执行线程

注:最新代码上传至https://github.com/csj50/webapp2.git

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值