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;
    }
    /**
     * 

* 更新订阅过期时间 *

*/ public void updateExpiration() { this.expiration = System.currentTimeMillis() + CONSUMER_INSTANCE_TIMEOUT_MS_DEFAULT; } /** *

* 到指定时间还有多久 *

*/ 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 consumersByExpiration = new PriorityQueue(
            new Comparator() {
                //小的在前
				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();
        }
    }
    /**
     * 

* 检查consumerInfo的过期时间,过期就从缓存中删除 *

* @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--高效的定时任务设计)