自己写的关于生产者与消费者模式,还有定时任务的demo
为了加深对生产者消费者模式的理解,特意写了这个demo,里面还包含了一个自己写的定时任务。代码下载地址:http://download.csdn.net/detail/li_yan_fei/9811572
是个maven项目,只用了spring框架。
学到的内容有3个
第一:加深了对生产者消费者模式的理解
第二:java Object 的wait() timeout数值如果等于0,则会造成线程一直等待下去,除非被notify唤醒
第三:java中main函数主线程死掉不会影响其他线程的正常执行(除了守护线程)。因为main函数主线程和其他非守护线程是一样的。
代码:
1.一个实现了按时间排序的队列
- package com.lyf.task;
-
- import java.lang.reflect.InvocationTargetException;
- import java.util.ArrayList;
- import java.util.List;
-
- import org.apache.commons.beanutils.BeanUtils;
-
- import com.lyf.bean.TaskInfo;
-
- /**
- * @ClassName: TaskStack
- * @Description: 实现了一个按照时间顺序排列的队列
- * @author yanfei.li
- * @date 2017年4月10日 下午1:53:49
- * @Company:
- */
- public class TaskStack {
-
- private List<TaskInfo> queue = new ArrayList<TaskInfo>();
-
- public TaskStack() {
- }
-
- /**
- * 功能描述: 往队列里塞任务
- * @Title: push
- * @author yanfei.li
- * @date 2017年4月11日 上午11:41:50
- * @param taskInfo
- * @return void
- * @throws
- */
- public synchronized void push(TaskInfo taskInfo) {
-
- // 如果是空队列,直接将任务放进去就可以了
- if (this.queue.isEmpty()) {
- this.queue.add(taskInfo);
- // 唤醒正在调用pop的消费者线程
- this.notify();
- return;
- }
- // 如果队列不是空的,就要比较执行时间了,根据执行时间排序
- for (int index = 0; index < this.queue.size(); index++) {
- TaskInfo info = this.queue.get(index);
- if (info.getRunTime() > taskInfo.getRunTime()) {
- this.queue.add(index, info);
- this.notify();
- return;
- }
- }
- }
-
- /**
- * 功能描述: 从队列里取任务
- * @Title: pop
- * @author yanfei.li
- * @date 2017年4月11日 上午11:41:28
- * @return TaskInfo
- * @throws
- */
- public synchronized TaskInfo pop() {
-
- // 如果队列里没有,就释放锁,等待唤醒
- if (this.queue.isEmpty()) {
- try {
- this.wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
-
- while (true) {
- TaskInfo taskInfo = this.queue.get(0);
- Long now = System.currentTimeMillis();
- if (now >= taskInfo.getRunTime()) {// 如果取出的任务到了执行的时间了,就返回该任务并且从队列中移除此任务。这里一定要注意了 判断条件一定要有=号,因为很可能出现相等的情况,如果进入了else中,就会造成wait(0).如果没有notify就一直等下去了
- this.queue.remove(0);
- return taskInfo;
- } else {
- try {
- System.out.println("pop--------" + (taskInfo.getRunTime() - now));
- this.wait(taskInfo.getRunTime() - now);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
-
- /**
- * 功能描述: 从队列里移除任务
- * @Title: remove
- * @author yanfei.li
- * @date 2017年4月11日 上午11:41:06
- * @param taskInfo
- * @return void
- * @throws
- */
- public synchronized void remove(TaskInfo taskInfo) {
- this.queue.remove(taskInfo);
- }
-
- /**
- * 功能描述: 返回队列里所有的任务
- * @Title: getAll
- * @author yanfei.li
- * @date 2017年4月11日 上午11:40:36
- * @return List<TaskInfo>
- * @throws
- */
- public synchronized List<TaskInfo> getAll() {
- List<TaskInfo> retList = new ArrayList<TaskInfo>();
- for (TaskInfo taskInfo : this.queue) {
- try {
- TaskInfo retTask = new TaskInfo();
- BeanUtils.copyProperties(retTask, taskInfo);
- retList.add(retTask);
- } catch (IllegalAccessException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
- }
- return retList;
- }
- }
2.消费者
- package com.lyf.task;
-
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
-
- import com.lyf.bean.TaskInfo;
- import com.lyf.util.SpringContextUtils;
-
- /**
- * @ClassName: TaskExecutor
- * @Description: 任务执行器,相当于消费者
- * @author yanfei.li
- * @date 2017年4月10日 下午1:52:00
- * @Company:
- *
- */
- public class TaskExecutor implements Runnable {
-
- private TaskStack taskStack = null;//任务队列
- private ExecutorService fixedThreadPool = null;//线程执行池,选择的固定线程池大小,由sping初始化
-
- public TaskExecutor(TaskStack taskStack,int poolSize) {
- this.taskStack = taskStack;
- this.fixedThreadPool = Executors.newFixedThreadPool(poolSize);
- }
-
- public void run() {
-
- while(true){
- try {
- //获取当前要执行的任务
- TaskInfo taskInfo = taskStack.pop();
- //解析taskinfo信息,获取真正执行的task
- TaskInterface instance = (TaskInterface)SpringContextUtils.getBean(taskInfo.getApi());
- //将任务信息,传递给将要执行的task
- instance.setTaskInfo(taskInfo);
- //将任务放到线程池中执行
- this.fixedThreadPool.submit(instance);
- //如果是周期性的任务,则计算出下次执行时间,然后放到队列中
- if(instance.hasNext()){
- this.taskStack.push(instance.next());
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- }
-
- }
3.任务bean
- package com.lyf.bean;
-
- import java.io.Serializable;
-
- /**
- * @ClassName: TaskInfo
- * @Description: 任务实体
- * @author yanfei.li
- * @date 2017年4月10日 下午1:54:58
- * @Company:
- *
- */
- public class TaskInfo implements Serializable {
-
- private static final long serialVersionUID = 8609311967819063807L;
-
- private String id;// 任务id
-
- private String type;// 任务类型
-
- private String runAt;// 执行时间规则
-
- private String cron;// cron表达式
-
- private long runTime;// 执行时间
-
- private String api;// 执行接口
-
- private Object[] params;// 任务参数
-
- public String getId() {
- return id;
- }
-
- public void setId(String id) {
- this.id = id;
- }
-
- public String getType() {
- return type;
- }
-
- public void setType(String type) {
- this.type = type;
- }
-
- public String getRunAt() {
- return runAt;
- }
-
- public void setRunAt(String runAt) {
- this.runAt = runAt;
- }
-
- public String getCron() {
- return cron;
- }
-
- public void setCron(String cron) {
- this.cron = cron;
- }
-
- public long getRunTime() {
- return runTime;
- }
-
- public void setRunTime(long runTime) {
- this.runTime = runTime;
- }
-
- public String getApi() {
- return api;
- }
-
- public void setApi(String api) {
- this.api = api;
- }
-
- public Object[] getParams() {
- return params;
- }
-
- public void setParams(Object[] params) {
- this.params = params;
- }
-
- }
4.任务接口
- package com.lyf.task;
-
- import com.lyf.bean.TaskInfo;
-
- /**
- * @ClassName: TaskInterface
- * @Description: TODO
- * @author yanfei.li
- * @date 2017年4月11日 下午12:00:10
- * @Company:
- *
- */
- public interface TaskInterface extends Runnable {
-
- /**
- * 功能描述: 设置任务
- * @Title: setTaskInfo
- * @author yanfei.li
- * @date 2017年4月11日 下午12:03:28
- * @param taskInfo
- * @return void
- * @throws
- */
- public void setTaskInfo(TaskInfo taskInfo);
- /**
- * 功能描述: 判断此任务是否还需要执行,针对的是循环性的任务
- * @Title: hasNext
- * @author yanfei.li
- * @date 2017年4月11日 下午12:02:14
- * @return boolean
- * @throws
- */
- public boolean hasNext();
- /**
- * 功能描述: 返回下次要执行的任务
- * @Title: next
- * @author yanfei.li
- * @date 2017年4月11日 下午12:02:18
- * @return TaskInfo
- * @throws
- */
- public TaskInfo next();
- }
5.实现了任务接口的任务抽象模板类- package com.lyf.task;
-
- import com.lyf.bean.TaskInfo;
- import com.lyf.util.SpringCronResolveUtil;
-
- /**
- * @ClassName: TaskInstance
- * @Description: 任务执行的模板类
- * @author yanfei.li
- * @date 2017年4月11日 下午2:47:57
- * @Company:
- *
- */
- public abstract class TaskInstance implements TaskInterface {
-
- protected TaskInfo taskInfo;
-
-
- public void run() {
-
- try {
- this.before();
- this.execute();
- this.after();
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } finally {
- //如果要记录日志什么的,可以放到这里
- }
-
- }
-
- /**
- * 功能描述: 任务执行前执行的方法,比如做一些初始化工作
- * @Title: before
- * @author yanfei.li
- * @date 2017年4月11日 下午2:50:43
- * @return void
- * @throws
- */
- protected abstract void before() throws Exception;
-
- /**
- * 功能描述: 任务执行的具体方法
- * @Title: excute
- * @author yanfei.li
- * @date 2017年4月11日 下午2:51:25
- * @return void
- * @throws
- */
- protected abstract void execute() throws Exception;
-
- /**
- * 功能描述: 任务执行完后执行的方法
- * @Title: after
- * @author yanfei.li
- * @date 2017年4月11日 下午2:51:41
- * @return void
- * @throws
- */
- protected abstract void after() throws Exception;
-
-
- public void setTaskInfo(TaskInfo taskInfo) {
- this.taskInfo = taskInfo;
- }
-
- public boolean hasNext() {
- if (this.taskInfo != null && !this.taskInfo.getRunAt().equals("now")) {
- return true;
- }
- return false;
- }
-
- public TaskInfo next() {
- if (this.taskInfo != null) {
- this.taskInfo.setRunTime(
- SpringCronResolveUtil.nextExecutionTime(this.taskInfo.getCron(), this.taskInfo.getRunTime()));
- System.out.println("next===========" + (taskInfo.getRunTime() - System.currentTimeMillis()));
- return this.taskInfo;
- }
- return null;
- }
-
- }
6.具体任务实现类
- package com.lyf.task;
-
- import org.springframework.context.annotation.Scope;
- import org.springframework.stereotype.Service;
-
- @Service("MyTask")
- @Scope("prototype")
- public class MyTask extends TaskInstance {
-
- @Override
- protected void before() throws Exception {
- System.out.println("MyTask--------------before-------------");
-
- }
-
- @Override
- protected void execute() throws Exception {
- System.out.println("MyTask--------------execute-------------");
-
- }
-
- @Override
- protected void after() throws Exception {
- System.out.println("MyTask--------------after-------------");
- }
-
- }
7.任务service
- package com.lyf.task;
-
- import com.lyf.bean.TaskInfo;
-
- public interface TaskService {
-
- public void startTask(TaskInfo task);
- }
8.实现类
- package com.lyf.task;
-
- import com.lyf.bean.TaskInfo;
- import com.lyf.util.SpringCronResolveUtil;
-
- public class TaskServiceImpl implements TaskService{
-
- private TaskStack taskStack = null;
- private TaskExecutor executor = null;
-
- public TaskServiceImpl(Integer poolSize) {
- this.taskStack = new TaskStack();
- if(poolSize != null){
- executor = new TaskExecutor(this.taskStack, poolSize);
- new Thread(executor).start();
- }
- this.init();
- }
-
- private void init(){
- //做一些其他的初始化工作
- }
-
- public void startTask(TaskInfo task) {
-
- if (task == null) {
- return;
- }
-
- //首次执行,设置runTime
- if (task.getRunAt().equals("now")) {
- task.setRunTime(System.currentTimeMillis() - 1);
- } else {
- if (task.getCron() == null || "".equals(task.getCron().trim())) {
- return;
- }
- task.setRunTime(SpringCronResolveUtil.nextExecutionTime(task.getCron()));
- }
- this.taskStack.push(task);
- }
-
- }
9.从spring容器中获取bean的工具类(在多线程环境下使用spring注解无法注入bean,所以需要手动获取bean)
- package com.lyf.util;
-
- import org.springframework.beans.BeansException;
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.ApplicationContextAware;
-
- public class SpringContextUtils implements ApplicationContextAware {
-
- private static ApplicationContext applicationContext = null;
- /**
- * 当继承了ApplicationContextAware类之后,那么程序在调用
- * getBean(String)的时候会自动调用该方法,不用自己操作
- */
- public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
- SpringContextUtils.applicationContext = applicationContext;
- }
-
- public static ApplicationContext getApplicationContext(){
- return SpringContextUtils.applicationContext;
- }
- /**
- * 功能描述: 根据name从spring容器中返回bean
- * @Title: getBean
- * @author yanfei.li
- * @date 2017年4月11日 下午1:47:17
- * @param name
- * @throws BeansException
- * @return Object
- */
- public static Object getBean(String name)throws BeansException{
- return applicationContext.getBean(name);
- }
- /**
- * 功能描述: 根据name和类型从spring容器中返回bean
- * @Title: getBean
- * @author yanfei.li
- * @date 2017年4月11日 下午1:46:46
- * @param name
- * @param requireType
- * @throws BeansException
- * @return Object
- */
- public static <T> Object getBean(String name,Class<T> requireType)throws BeansException{
- return applicationContext.getBean(name,requireType);
- }
-
- }
10.解析cron表达式的util(表达式只能是以空格分割的包含6个字符的字符串,不然会报错)
- package com.lyf.util;
-
- import java.util.Date;
-
- import org.springframework.scheduling.support.CronSequenceGenerator;
-
- /**
- * @ClassName: SpringCronResolveUtil
- * @Description: 解析cron字符串的工具类
- * @author yanfei.li
- * @date 2017年4月11日 下午2:59:44
- * @Company:
- *
- */
- public class SpringCronResolveUtil {
-
- /**
- * 功能描述: 根据当前时间计算并返回下次执行时间
- * @Title: nextExecutionTime
- * @author yanfei.li
- * @date 2017年4月11日 下午3:00:56
- * @param cron 表达式字符串,包含6个以空格分开的字符
- * @return long
- * @throws
- */
- public static long nextExecutionTime(String cron){
- CronSequenceGenerator cronSequenceGenerator = new CronSequenceGenerator(cron);
- Date lastTime = new Date();
- Date nexDate = cronSequenceGenerator.next(lastTime);
-
- return nexDate.getTime();
-
- }
-
- /**
- * 功能描述: 根据最后一次执行时间计算并返回下次执行时间
- * @Title: nextExecutionTime
- * @author yanfei.li
- * @date 2017年4月11日 下午3:00:23
- * @param cron 表达式字符串,一定要是包含6个以空格分离的字符
- * @param lastTime 最近的执行时间
- * @return long
- * @throws
- */
- public static long nextExecutionTime(String cron,long lastTime) {
- Date date = new Date(lastTime);
- CronSequenceGenerator cronSequenceGenerator = new CronSequenceGenerator(cron);
- Date nexDate = cronSequenceGenerator.next(date);
- return nexDate.getTime();
- }
-
- public static void main(String[] args) {
- String cron = "0/10 * * * * ? ";
- System.out.println("当前时间:" + new Date().getTime());
- System.out.println("下一次时间:" + nextExecutionTime(cron));
- }
- }
11.测试
- package com.lyf.producerandconsumer;
-
- import org.springframework.context.ApplicationContext;
- import org.springframework.context.support.ClassPathXmlApplicationContext;
-
- import com.lyf.bean.TaskInfo;
- import com.lyf.task.TaskService;
- import com.lyf.util.SpringContextUtils;
-
- /**
- * @ClassName: TestMain
- * @Description: 测试
- * @author yanfei.li
- * @date 2017年4月11日 下午6:20:30
- * @Company:
- *
- */
- public class TestMain {
-
- public static void main(String[] args) throws InterruptedException {
- System.out.println("---------------");
- ApplicationContext ac = new ClassPathXmlApplicationContext("com/lyf/task/local-spring.xml");
- TaskInfo taskInfo = new TaskInfo();
- taskInfo.setApi("MyTask");
- taskInfo.setRunAt("some");
- taskInfo.setCron("0/10 * * * * ?");//每隔10秒执行一次
- taskInfo.setType("me");
- System.out.println("++++++++++++++++" + ac);
- TaskService taskService = (TaskService) SpringContextUtils.getBean("TaskService");
- taskService.startTask(taskInfo);
-
- System.out.println("===========" + taskService);
-
- //主线程的死掉,不会影响其他线程的继续执行,除非是守护线程。
-
- }
-
- }
12.pom.xml
- <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.lyf</groupId>
- <artifactId>producerandconsumer</artifactId>
- <version>0.0.1-SNAPSHOT</version>
- <packaging>jar</packaging>
-
- <name>producerandconsumer</name>
- <url>http://maven.apache.org</url>
-
- <properties>
- <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- </properties>
-
- <dependencies>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <version>3.8.1</version>
- <scope>test</scope>
- </dependency>
-
- <!-- https://mvnrepository.com/artifact/commons-beanutils/commons-beanutils -->
- <dependency>
- <groupId>commons-beanutils</groupId>
- <artifactId>commons-beanutils</artifactId>
- <version>1.8.3</version>
- </dependency>
- <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-aop</artifactId>
- <version>4.0.4.RELEASE</version>
- </dependency>
-
- <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-beans</artifactId>
- <version>4.0.4.RELEASE</version>
- </dependency>
-
-
- <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-context</artifactId>
- <version>4.0.4.RELEASE</version>
- </dependency>
-
- <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-core</artifactId>
- <version>4.0.4.RELEASE</version>
- </dependency>
- <!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-tx</artifactId>
- <version>4.0.4.RELEASE</version>
- </dependency>
-
- <!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-web</artifactId>
- <version>4.0.4.RELEASE</version>
- </dependency>
- <!-- https://mvnrepository.com/artifact/org.springframework/spring-expression -->
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-expression</artifactId>
- <version>4.0.4.RELEASE</version>
- </dependency>
- <!-- https://mvnrepository.com/artifact/aopalliance/aopalliance -->
- <dependency>
- <groupId>aopalliance</groupId>
- <artifactId>aopalliance</artifactId>
- <version>1.0</version>
- </dependency>
-
- <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
- <dependency>
- <groupId>org.aspectj</groupId>
- <artifactId>aspectjweaver</artifactId>
- <version>1.8.0</version>
- </dependency>
- <!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
- <dependency>
- <groupId>commons-logging</groupId>
- <artifactId>commons-logging</artifactId>
- <version>1.1.3</version>
- </dependency>
- </dependencies>
- </project>
13.spring.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:tx="http://www.springframework.org/schema/tx"
- xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
- xmlns:aop="http://www.springframework.org/schema/aop"
- 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/tx http://www.springframework.org/schema/tx/spring-tx.xsd
- http://www.springframework.org/schema/aop
- http://www.springframework.org/schema/aop/spring-aop.xsd ">
-
- <!-- 启动注解扫描 -->
- <context:annotation-config/>
- <!-- 指定扫描的路径 -->
- <context:component-scan base-package="com.lyf.*" >
- <!-- 不对controller的注解 做处理,过滤掉,是为了和springmvc整合时,防止重复扫描,造成bean初始化2次-->
- <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
- </context:component-scan>
-
- <bean id="springUtils" class="com.lyf.util.SpringContextUtils"/>
-
- <bean name="TaskService" class="com.lyf.task.TaskServiceImpl">
- <constructor-arg>
- <value>5</value>
- </constructor-arg>
- </bean>
-
-
- </beans>
14.测试结果(每隔十秒执行一次,只贴出了部分打印数据)
- ++++++++++++++++org.springframework.context.support.ClassPathXmlApplicationContext@23e352bf: startup date [Wed Apr 12 11:44:46 GMT+08:00 2017]; root of context hierarchy
- ===========com.lyf.task.TaskServiceImpl@2f54745e
- pop--------3621
- MyTask--------------before-------------
- MyTask--------------execute-------------
- MyTask--------------after-------------
- next===========9997
- pop--------9997
- MyTask--------------before-------------
- MyTask--------------execute-------------
- MyTask--------------after-------------
- next===========9999
- pop--------9999
- MyTask--------------before-------------
- MyTask--------------execute-------------
- MyTask--------------after-------------