其他
Android 线程池周期性任务踩坑探究
本文作者:卓修武,原文发布于:编程物语。
public class MyHandler extends Handler {
private long lastExecTime = System.nanoTime();
private long delayTime = 50 * 1000000; // 转换为纳秒
@Override
public void handleMessage(Message msg) {
long startTime = System.nanoTime();
// 执行任务
doTask();
// 计算下一次任务执行的延迟时间
long timeDiff = System.nanoTime() - lastExecTime;
long nextDelayTime = Math.max(delayTime - timeDiff, 0);
// 记录本次执行时间和延迟时间
lastExecTime = System.nanoTime();
delayTime = nextDelayTime;
// 发送下一次任务消息
sendEmptyMessageDelayed(0, delayTime / 1000000); // 转换为毫秒
}
private void doTask() {
// 需要执行的任务
}
}
public interface ScheduledExecutorService extends ExecutorService {
public ScheduledFuture<?> schedule(Runnable command,
long delay, TimeUnit unit);
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit);
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
}
最终在Apm项目中我创建了一个全局通用的ScheduledExecutorService 对象来支持内部各个监控任务定时执行的需求,这里我最终使用的是 **scheduleAtFixedRate **来进行任务注册调度的。
在线下进行了多次自测后,未复现该问题。
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
public boolean offer(Runnable x) {
if (x == null)
throw new NullPointerException();
RunnableScheduledFuture<?> e = (RunnableScheduledFuture<?>)x;
final ReentrantLock lock = this.lock;
lock.lock();
try {
int i = size;
if (i >= queue.length)
grow();
size = i + 1;
if (i == 0) {
queue[0] = e;
setIndex(e, 0);
} else {
siftUp(i, e);
}
if (queue[0] == e) {
leader = null;
available.signal();
}
} finally {
lock.unlock();
}
return true;
}
private void siftUp(int k, RunnableScheduledFuture<?> key) {
while (k > 0) {
int parent = (k - 1) >>> 1;
RunnableScheduledFuture<?> e = queue[parent];
//基于优先级排序
if (key.compareTo(e) >= 0)
break;
//替换元素
queue[k] = e;
setIndex(e, k);
k = parent;
}
queue[k] = key;
setIndex(key, k);
}
public int compareTo(Delayed other) {
if (other == this) // compare zero if same object
return 0;
if (other instanceof ScheduledFutureTask) {
ScheduledFutureTask<?> x = (ScheduledFutureTask<?>)other;
long diff = time - x.time;
if (diff < 0)
return -1;
else if (diff > 0)
return 1;
else if (sequenceNumber < x.sequenceNumber)
return -1;
else
return 1;
}
long diff = getDelay(NANOSECONDS) - other.getDelay(NANOSECONDS);
return (diff < 0) ? -1 : (diff > 0) ? 1 : 0;
}
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (period <= 0L)
throw new IllegalArgumentException();
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
// 调用triggerTime 设置任务下一次执行的时间
triggerTime(initialDelay, unit),
unit.toNanos(period),
sequencer.getAndIncrement());
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
delayedExecute(t);
return t;
}
/**
* Returns the nanoTime-based trigger time of a delayed action.
*/
long triggerTime(long delay) {
return System.nanoTime() +
((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
}
/**
* Overrides FutureTask version so as to reset/requeue if periodic.
*/
public void run() {
boolean periodic = isPeriodic();
if (!canRunInCurrentRunState(periodic))
cancel(false);
else if (!periodic) //非周期性任务,直接调用
super.run();
else if (super.runAndReset()) { //周期性任务,调用runAndReset()
//更新任务的time属性
setNextRunTime();
//重新入队,等待下次调度
reExecutePeriodic(outerTask);
}
}
protected boolean runAndReset() {
if (state != NEW ||
!RUNNER.compareAndSet(this, null, Thread.currentThread()))
return false;
boolean ran = false;
int s = state;
try {
Callable<V> c = callable;
if (c != null && s == NEW) {
try {
c.call(); // don't set result
ran = true;
} catch (Throwable ex) {
setException(ex);
}
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
return ran && s == NEW;
}
/**
* Sets the next time to run for a periodic task.
*/
private void setNextRunTime() {
long p = period;
if (p > 0)
time += p;
else
time = triggerTime(-p);
}
scheduleAtFixedRate():计算任务下一次执行的时间,不是根据当前基准时间计算的,而是上一次任务设置的time的值。 scheduleWithFIxedDelay():计算任务下一次执行的时间,是以当前基准时间计算的。
Submits a periodic action that becomes enabled first after the given initial delay, and
subsequently with the given period; that is, executions will commence after initialDelay,
then initialDelay + period, then initialDelay + 2 * period, and so on.
任务初始执行的时间是基于设置的延迟时间开始执行的,之后每一次执行的时间也是基于该初始时间调度的, 比如初始时间是 1000,周期是1000, 那么后续任务的执行时间正常情况下应该是 **2000 3000 4000**。 而 shceduleAtFixedDelay 是以任务执行结束后,后System.nanotime()重新计算下次调度时间的。scheduleAtFixedRate()** **这种以任务初始时间作为计算任务调度时间会有什么情况的现象出现呢?
最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!
推荐阅读:
扫一扫 关注我的公众号
如果你想要跟大家分享你的文章,欢迎投稿~
┏(^0^)┛明天见!