我已经实现了FixedClockScheduledExecutorService 进行测试,希望对某些人有所帮助。要使用它,只需像往常一样安排任务,并在您想提前完成任务时致电elapse()。
(有一些小警告:没有实现排序,也没有实现关闭逻辑,并且它可能无法在极端的时间范围内工作。如有必要,这些都可以修复)。
class FixedClockScheduledExecutorService extends AbstractExecutorService implements ScheduledExecutorService {
public FixedClockScheduledExecutorService() {}
private final Collection<Job<?>> jobs = new CopyOnWriteArrayList<>(); //Collection must support concurrent modification. TODO: Needs ordering
private long offsetNanos = 0;
//Call this to advance the clock...
public void elapse(long time, TimeUnit timeUnit) {
offsetNanos += NANOSECONDS.convert(time, timeUnit);
for(Job<?> job: jobs) {
if(offsetNanos >= job.initialDelayNanos) {
jobs.remove(job);
job.run();
}
}
}
private <V> ScheduledFuture<V> scheduleIntenal(Callable<V> callable, long delay, long period, TimeUnit timeUnit) {
Job<V> job = new Job<V>(callable, offsetNanos + NANOSECONDS.convert( delay, timeUnit), NANOSECONDS.convert(period, timeUnit));
jobs.add(job);
return job;
}
@Override
public ScheduledFuture<?> schedule(Runnable runnable, long delay, TimeUnit timeUnit) {
return schedule(Executors.callable(runnable, null), delay, timeUnit);
}
@Override
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit timeUnit) {
return scheduleIntenal(callable, delay, 0, timeUnit);
}
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable runnable, long delay, long period, TimeUnit timeUnit) {
return scheduleIntenal(Executors.callable(runnable, null), delay, Math.abs(period), timeUnit);
}
@Override
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable runnable, long delay, long period, TimeUnit timeUnit) {
return scheduleIntenal(Executors.callable(runnable, null), delay, -Math.abs(period), timeUnit);
}
class Job<V> extends FutureTask<V> implements ScheduledFuture<V> {
final Callable<V> task;
final long initialDelayNanos;
final long periodNanos;
public Job(Callable<V> runner, long initialDelayNanos, long periodNanos) {
super(runner);
this.task = runner;
this.initialDelayNanos = initialDelayNanos;
this.periodNanos = periodNanos;
}
@Override public long getDelay(TimeUnit timeUnit) {return timeUnit.convert(initialDelayNanos, NANOSECONDS);}
@Override public int compareTo(Delayed delayed) {throw new RuntimeException();} //Need to implement this to fix ordering.
@Override public void run() {
if(periodNanos == 0) {
super.run();
} else {
//If this task is periodic and it runs ok, then reschedule it.
if(super.runAndReset()) {
jobs.add(reschedule(offsetNanos));
}
}
}
private Job<V> reschedule(long offset) {
if(periodNanos < 0) return new Job<V>(task, offset, periodNanos); //fixed delay
long newDelay = initialDelayNanos; while(newDelay <= offset) newDelay += periodNanos; //fixed rate
return new Job<V>(task, newDelay, periodNanos);
}
}
@Override public void execute(Runnable command) { schedule(command, 0, NANOSECONDS); }
@Override public void shutdown() {}
@Override public List<Runnable> shutdownNow() { throw new RuntimeException(); }
@Override public boolean isShutdown() { return false;}
@Override public boolean isTerminated() { return false;}
@Override public boolean awaitTermination(long timeout, TimeUnit unit) { return true; }
}