1.java个性化定时任务工具类
import java.util.concurrent.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RetryScheduler {
private static final ConcurrentMap<String, RetryTask> TASKS = new ConcurrentHashMap<String, RetryTask>();
private static final int EXE_THREAD_COUNT = 3;
private static ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 10, 50L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(100));
private static final int DEFAULT_DELAY = 3;
private static final int DEFAULT_INTERVAL = 3;
private static final int DEFAULT_RETRY_COUNT = 3;
// api
public static void schedule(String id, Runnable task) {
schedule(id, task, DEFAULT_DELAY, DEFAULT_INTERVAL, DEFAULT_RETRY_COUNT);
}
public static void schedule(String id, Runnable task, int retryDelay, int interval, int retryCount) {
RetryTask rtask = new RetryTask(id, task, retryDelay, interval, retryCount);
if (retryDelay == 0) {
executor.submit(rtask);
}
TASKS.put(id, rtask);
}
public static void cancel(String id) {
RetryTask task = TASKS.get(id);
if (task != null) {
task.cancel();
}
}
//
static {
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
for (RetryTask rtask : TASKS.values()) {
if (rtask.isDone()) {
rtask.cancel();
continue;
}
if (rtask.isTimeout()) {
rtask.updateNextExpire();
executor.submit(rtask);
}
}
}
}, 0, 1000, TimeUnit.MILLISECONDS);
}
static class RetryTask implements Runnable {
private Logger logger = LoggerFactory.getLogger(getClass());
// 最长执行时间
private static final long MAX_EXECUTE_TIME = 1000 * 60 * 10;
// 唯一标识
private String id;
private Runnable task;
//
private long deadLine;
// 上次重试完成时间
private long nextExpire;
// 重试间隔(单位毫秒)
private int interval;
// 已经重试完成次数
private volatile int exeCount;
// 最多重试次数
private int retryCount;
//
private int errCount;
public RetryTask(String id, Runnable task, int retryDelay, int interval, int retryCount) {
super();
this.id = id;
this.task = task;
this.nextExpire = System.currentTimeMillis() + (retryDelay + interval) * 1000;
this.interval = interval * 1000;
this.retryCount = retryCount;
// max execute time 10 minutes
deadLine = System.currentTimeMillis() + MAX_EXECUTE_TIME;
}
public void cancel() {
logger.info(id + " cancel");
TASKS.remove(id);
}
public boolean isDone() {
return (exeCount >= retryCount) || (errCount > 3) || isDead();
}
public boolean isTimeout() {
return System.currentTimeMillis() > nextExpire;
}
private void updateNextExpire() {
nextExpire += interval;
}
private boolean isDead() {
return (System.currentTimeMillis() > deadLine);
}
@Override
public void run() {
if (isDone()) {
cancel();
return;
}
logger.info("id={} timeout start retry index={} interval={} totalCount={}", id, exeCount + 1, interval, retryCount);
long start = System.currentTimeMillis();
try {
task.run();
} catch (Throwable e) {
logger.error(e.getMessage(), e);
++errCount;
} finally {
int costTime = (int) (System.currentTimeMillis() - start);
if (costTime > interval) {
interval = costTime;
}
++exeCount;
}
}
}
}
2.按key创建定时任务和取消任务
StringBuilder sequenceSb = new StringBuilder("testkey_").append(id).append("_");
RetryScheduler.schedule(sequenceSb.toString(), new Runnable() {
@Override
public void run() {
//判断是不是需要重复
String stateKey = new StringBuilder(sequenceSb).append("_state").toString();
String state = redisCache.get(stateKey);
if(state != null){
if("0".equals(state)) {
//如果发送成功取消定时任务
RetryScheduler.cancel(sequenceSb.toString());
}else if("-1".equals(state)) {
//没有成功继续执行
}
}
}
}, 0, 5, 100);//每隔5s执行一次,最多执行100次