学习笔记- 自己写的关于生产者与消费者模式,还有定时任务的demo。

这篇学习笔记通过一个包含生产者消费者模式的Java demo,深入理解该模式,同时介绍了一个自定义的定时任务。项目采用maven和Spring框架,亮点包括对wait()超时问题的探讨及非守护线程独立执行特性。附带代码资源链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

为了加深对生产者消费者模式的理解,特意写了这个demo,里面还包含了一个自己写的定时任务。代码下载地址:http://download.youkuaiyun.com/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-------------



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值