Java--高效的定时任务设计
相信你在日常的开发中肯定遇到过这种问题: 需要对实体类的状态信息进行管理,比如一定时间后修改它为XXX状态.
举个例子: 订单服务,当用户提交了订单后,如果在30分钟内没有支付,自动取消订单,这就是一个对状态的管理;
再举一个我实际开发的例子: 消息管道的例子,用户来拉取消息后,如果在30s内没有提交,那么修改他的订阅状态为:未订阅,这样其他的实例可以建立连接继续读取.
整理设计图:
核心就是: 一个Thread + 一个Queue;Thread不断从队列中取出数据, 如果队列中为空或者里边的任务没到期,则线程卡住wait(timeOut).
二 详细设计
先是简单的有状态的实体类:ConsumerInfoState,这个类的核心是状态(订阅到期时间),所以得有对状态的查询设置,查询距到期还要多久等等....
import java.io.Serializable;
public class ConsumerInfoState implements Serializable {
/**
* 序列化ID
*/
private static final long serialVersionUID = 1L;
/**
* 过期时间20s
*/
protected long expiration;
private String topic;
private String userId;
private boolean isSubscribed = false;
private long CONSUMER_INSTANCE_TIMEOUT_MS_DEFAULT = 5000;
public ConsumerInfoState(String userId) {
this.userId = userId;
this.expiration = System.currentTimeMillis() + CONSUMER_INSTANCE_TIMEOUT_MS_DEFAULT;
}
public ConsumerInfoState(String topic, String userId) {
super();
this.topic = topic;
this.userId = userId;
this.expiration = System.currentTimeMillis() + CONSUMER_INSTANCE_TIMEOUT_MS_DEFAULT;
}
/**
*是否过期
*/
public boolean expired(long nowMs) {
return expiration <= nowMs;
}
/**
* <p>
* 更新订阅过期时间
* </p>
*/
public void updateExpiration() {
this.expiration = System.currentTimeMillis() + CONSUMER_INSTANCE_TIMEOUT_MS_DEFAULT;
}
/**
* <p>
* 到指定时间还有多久
* </p>
*/
public long untilExpiration(long nowMs) {
return this.expiration - nowMs;
}
public String getUserId() {
return userId;
}
public String getTopic() {
return topic;
}
public void setTopic(String topic) {
this.topic = topic;
}
public void setSubscribed(boolean isSubscribed) {
this.isSubscribed = isSubscribed;
}
public boolean hasSubscribed() {
return isSubscribed;
}
}
这个类还是很清晰的..
核心类: ConsumerInfoManager
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; public class ConsumerInfoManager {
Logger logger = LoggerFactory.getLogger(ConsumerInfoManager.class);
//任务队列
private final PriorityQueue<ConsumerInfoState> consumersByExpiration = new PriorityQueue<ConsumerInfoState>(
new Comparator<ConsumerInfoState>() {
//小的在前
public int compare(ConsumerInfoState o1, ConsumerInfoState o2) {
if (o1.expiration < o2.expiration) {
return -1;
} else if (o1.expiration == o2.expiration) {
return 0;
} else {
return 1;
}
}
}); private ExpirationThread expirationThread; public ConsumerInfoManager() {
//启动线程
this.expirationThread = new ExpirationThread();
this.expirationThread.start();
}
//加入任务队列
public synchronized void addConsumerInfoSate(ConsumerInfoState consumerInfoSate) {
consumersByExpiration.add(consumerInfoSate);
this.notifyAll();
} @SuppressWarnings("unused")
public synchronized void updateExpiration(ConsumerInfoState state) {
// 先删除在放里边,重新排序
consumersByExpiration.remove(state);
state.updateExpiration();
consumersByExpiration.add(state);
this.notifyAll();
} public void shutdown() {
logger.debug("Shutting down consumers");
expirationThread.shutdown();
synchronized (this) {
consumersByExpiration.clear();
}
}
/**
* <p>
* 检查consumerInfo的过期时间,过期就从缓存中删除
* </p>
* @author jiangyuechao 2018年1月13日 下午2:04:30
*/
@SuppressWarnings("unused")
private class ExpirationThread extends Thread {
AtomicBoolean isRunning = new AtomicBoolean(true);
CountDownLatch shutdownLatch = new CountDownLatch(1);
public ExpirationThread() {
super("Consumer Expiration Thread");
setDaemon(true);
}
@Override
public void run() {
synchronized (ConsumerInfoManager.this) {
try {
while (isRunning.get()) {
long now = System.currentTimeMillis();
//队列空和最近一个任务是否到期的判断
while (!consumersByExpiration.isEmpty() && consumersByExpiration.peek().expired(now)) {
final ConsumerInfoState state = consumersByExpiration.remove();
//{你自己的业务处理}
state.setSubscribed(false);
logger.info("任务已到期,topic:{}, userID:{},subscribed:{}",state.getTopic(),state.getUserId(),state.hasSubscribed());
}
//需要等待的时间
long timeout = consumersByExpiration.isEmpty() ? Long.MAX_VALUE
: consumersByExpiration.peek().untilExpiration(now);
ConsumerInfoManager.this.wait(timeout);
}
} catch (InterruptedException e) {
// Interrupted by other thread, do nothing to allow this thread to exit
logger.error("ExpirationThread线程中断", e);
}
}
shutdownLatch.countDown();
}
public void shutdown() {
try {
isRunning.set(false);
this.interrupt();
shutdownLatch.await();
} catch (InterruptedException e) {
throw new Error("Interrupted when shutting down consumer worker thread.");
}
}
}
public void join(){
try {
expirationThread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
代码就这些,我进行了删减,删除了不重要的部分, 一般ConsumerInfoManager还需要一个缓存Cache,Cache中是存储所有的实体类,queue中是Cache中的一部分,一般queue中的任务到期,需要从Cache中删除或取出执行一些操作.
当然加Cache是复杂点的,核心思想就这些,额外的代码就删除了..
最后测试一下
public class ManagerTest {
static ConsumerInfoManager consumerInfoManager;
static String userId = "dhsajkdsajkdsjh1";
static Logger logger = LoggerFactory.getLogger(ManagerTest.class);
public static void main(String[] args) throws InterruptedException {
//实例化
setUp();
for(int i = 0;i<3;i++){
ConsumerInfoState consumerInfoState = new ConsumerInfoState("chao-"+i, userId);
consumerInfoState.setSubscribed(true);
consumerInfoManager.addConsumerInfoSate(consumerInfoState);
logger.info("任务"+i+"加入队列");
Thread.sleep(1000);
}
consumerInfoManager.join();
}
public static void setUp(){
consumerInfoManager = new ConsumerInfoManager();
}
}
输出结果: 符合预期...
2018-01-17 10:07:27,450 [main] INFO ManagerTest - 任务0加入队列
2018-01-17 10:07:28,451 [main] INFO ManagerTest - 任务1加入队列
2018-01-17 10:07:29,451 [main] INFO ManagerTest - 任务2加入队列
2018-01-17 10:07:32,451 [Consumer Expiration Thread] INFO ConsumerInfoManager - 任务已到期,topic:chao-0, userID:dhsajkdsajkdsjh1,subscribed:false
2018-01-17 10:07:33,485 [Consumer Expiration Thread] INFO ConsumerInfoManager - 任务已到期,topic:chao-1, userID:dhsajkdsajkdsjh1,subscribed:false
2018-01-17 10:07:34,452 [Consumer Expiration Thread] INFO ConsumerInfoManager - 任务已到期,topic:chao-2, userID:dhsajkdsajkdsjh1,subscribed:false
转发请注明出处: http://www.cnblogs.com/jycboy/p/8301538.html
Java--高效的定时任务设计的更多相关文章
- 基于Java反射的定时任务设计
一.使用场景 1.不需要立即执行.立即得到结果返回. 2.如果执行失败.需要有失败补偿机制. 3.和业务代码解耦,适用于不同的务场景. 4.调用接口的入参.出参 统计,方便查询. 二.执行顺序 1.业 ...
- 转:二十七、Java图形化界面设计——容器(JFrame)
转:http://blog.csdn.net/liujun13579/article/details/7756729 二十七.Java图形化界面设计——容器(JFrame) 程序是为了方便用户使用的, ...
- Java中异常处理和设计
在程序设计中,进行异常处理是非常关键和重要的一部分.一个程序的异常处理框架的好坏直接影响到整个项目的代码质量以及后期维护成本和难度.试想一下,如果一个项目从头到尾没有考虑过异常处理,当程序出错从哪里寻 ...
- 二十七、Java图形化界面设计——容器(JFrame)
摘自http://blog.csdn.net/liujun13579/article/details/7756729 二十七.Java图形化界面设计--容器(JFrame) 程序是为了方便用户使用的, ...
- Java图形化界面设计——容器(JFrame)
Java图形化界面设计——容器(JFrame) 程序是为了方便用户使用的,因此实现图形化界面的程序编写是所有编程语言发展的必然趋势,在命令提示符下运行的程序可以让我们了解java程序的基本知识体系结构 ...
- 01 Java图形化界面设计——容器(JFrame)
程序是为了方便用户使用的,因此实现图形化界面的程序编写是所有编程语言发展的必然趋势,在命令提示符下运行的程序可以让我们了解java程序的基本知识体系结构,现在就进入java图形化界面编程. 一.Jav ...
- Java基础-继承-编写一个Java应用程序,设计一个汽车类Vehicle,包含的属性有车轮个数 wheels和车重weight。小车类Car是Vehicle的子类,其中包含的属性有载人数 loader。卡车类Truck是Car类的子类,其中包含的属性有载重量payload。每个 类都有构造方法和输出相关数据的方法。最后,写一个测试类来测试这些类的功 能。
#29.编写一个Java应用程序,设计一个汽车类Vehicle,包含的属性有车轮个数 wheels和车重weight.小车类Car是Vehicle的子类,其中包含的属性有载人数 loader.卡车类T ...
- Java高效读取大文件
1.概述 本教程将演示如何用Java高效地读取大文件.这篇文章是Baeldung (http://www.baeldung.com/) 上“Java——回归基础”系列教程的一部分. 2.在内存中读取 ...
- 三十二、Java图形化界面设计——布局管理器之CardLayout(卡片布局)
摘自 http://blog.csdn.net/liujun13579/article/details/7773945 三十二.Java图形化界面设计--布局管理器之CardLayout(卡片布局) ...
- 三十三、Java图形化界面设计——布局管理器之null布局(空布局)
摘自http://blog.csdn.net/liujun13579/article/details/7774267 三十三.Java图形化界面设计--布局管理器之null布局(空布局) 一般容器都有 ...
随机推荐
- 【quickhybrid】API的分类:短期API、长期API
前言 一切就绪,开始规划API,这里在规划前对API进行了一次分类:短期API.长期API 首先申明下,这个是在实际框架演变过程中自创的一个概念,其它混合框架可能也会有这个概念,但应该是会在原生底层来 ...
- 一位10年Java工作经验的架构师聊Java和工作经验
从事近十年的 JavaEE 应用开发工作,现任阿里巴巴公司系统架构师.对分布式服务架构与大数据技术有深入研究,具有丰富的 B/S 架构开发经验与项目实战经验,擅长敏捷开发模式.国内开源软件推动者之一, ...
- 一、Spring Cloud介绍
springcloud 介绍 研究了一段时间spring boot了准备向spirng cloud进发,公司架构和项目也全面拥抱了Spring Cloud.在使用了一段时间后发现Spring Clou ...
- 45、SQL逻辑查询语句执行顺序
一 SELECT语句关键字的定义顺序 SELECT DISTINCT <select_list> FROM <left_table> <join_type> JOI ...
- rwx读写执行对文件和目录的意义
文件 目录 r 查看 列出目录内容 w 修改 在目录内新建删除文件 x 执行 可以进入目录 对文件的删除权限是对文件所有目录的写权限 对目录-wx的权限,有写和执行权限,既可以在目录内创建删除文件,可 ...
- go实例之轻量级线程goroutine、通道channel与select
1.goroutine线程 goroutine是一个轻量级的执行线程.假设有一个函数调用f(s),要在goroutine中调用此函数,请使用go f(s). 这个新的goroutine将与调用同时执行 ...
- Win10关闭某程序的通知的方法
一.点击右下角的通知图标. 二.点击所有通知. 三.点击系统 四.点击通知和操作 五.下拉,看到:获取来自这些发送者的通知 六.关闭自己想关闭通知的程序即可.
- xml文件生成方式一(字符串拼接,将多实体类对象写入xml文件)
1.xml文件生成,拼接字符串使用StringBuffer或StringBuilder 2.拼接好后写入文件即可,将多个实体类写入xml文件 3.这种方式比较简单,但是操作也比较麻烦 4.下面是我的代 ...
- 新手了解.Nat
1.Net平台 -->.Net平台 -->.Net FrameWrok框架 2.C#编程语言 -->编程语言:计算机能听懂的语言 -->使用.Net平台 C#是在.NEt平台 ...
- ABP架构学习系列一 整体项目结构及目录
本系列是基于aspnetboilerplate-0.8.4.0版本写的,其中原因是由于较高的版本太抽象难以理解和分析,对于还菜菜的我要花更多的时间去学习. abp的源码分析学习主要来源于 HK Zha ...