自己写的关于生产者与消费者模式,还有定时任务的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-------------



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值