我的目标是安排以非均匀费率发生的经常性工作。我将从第一个片段迁移到第二个片段:
第一
Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG1) {
//recurrent job here
long nextTime = nextTime();
sendMessageAtTime(obtainMessage(MSG1), nextTime);
}
}
}
};
第二
ScheduledExecutorService mExecutor;
while (true){
mExecutor.schedule(new Callable() {
public Object call() throws Exception {
long startTime = SystemClock.uptimeMillis();
//recurrent job here
delay = nextTime() - startTime ;
return true;
}
}, delay, TimeUnit.MILLISECONDS);
}
我的问题是:
1-在第一个片段中,mHandler引用的线程在作业之间是否可以自由执行其他任务或处理其他消息?
2-但是在第二个片段中,Thread总是忙于循环。正确?
3-如何重写第二个代码,以免我在作业之间松散线程活动(延迟)?
非常感谢任何帮助
答案 0 :(得分:1)
您的第二个代码无法按预期工作。在第一个任务被安排并等待执行之后,while
循环继续安排更多任务,所有任务都具有相同的延迟。所以你最终会有数千个,可能是数百万个任务。当然,因为主线程在没有任何等待的情况下运行无限循环,所以它一直很忙。这可能不是你想要的。
你最好使用simliar方法而不是上面的处理程序:
final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
executor.schedule(() -> {
// do work
// reschedule
executor.schedule(this, nextTime() - System.currentTimeMillis());
}, delay, TimeUnit.MILLISECONDS);
(当然,您还应检查重新安排时指定的延迟是否为负值。)
更新:如果您需要单独处理每个执行的结果,可能会出现与您的第二个代码示例类似的其他方法。它会调度任务执行,并将结果移交给Consumer
,只要它可用。 (注意循环内的future.get()
导致循环线程暂停直到任务完成。)
public static <T> void schedule(ScheduledExecutorService scheduler,
Schedule schedule, Callable<T> task, Consumer<? super T> consumer)
throws InterruptedException, ExecutionException {
while (true) {
if (Thread.interrupted()) throw new InterruptedException();
long delay = schedule.nextTime() - System.currentTimeMillis();
if (delay < 0) continue; // skip this step
ScheduledFuture<? extends T> future = scheduler.schedule(task,
delay, schedule.getUnit());
consumer.accept(future.get());
}
}
还要注意中断检查,以便其他线程可以通过中断循环线程来停止执行。这样可以简化此方法在另一个任务中的使用,以防您想在后台线程上运行它。
Schedule
可以是一个功能界面,提供对计划信息的访问:
@FunctionalInterface
public interface Schedule {
long nextTime();
default TimeUnit getUnit() { return TimeUnit.MILLISECONDS; }
}
顺便说一下:android.os.Handler
是一个非常好的方式来做你想要的android。因此,如果您确实需要其功能(例如,获得ScheduledExecutorService
结果),则只应迁移到Future
。
答案 1 :(得分:0)
public class RecurrentJobThatHappensOnANonEvenRate {
/**
* Consider you have your job defined as below
*/
abstract class TheJob implements Runnable {
@Override
public void run() {
long startTime = System.currentTimeMillis();
doRecurrentJob();
schedule(nextTime() - startTime);
}
void doRecurrentJob() {
// Do the job
}
long nextTime() {
// calculate next execution time
long randomDelay = Math.round(5000 + Math.random() * 5000);
return System.currentTimeMillis() + randomDelay;
}
public abstract void schedule(long delay);
};
/**
* Example using `ScheduledExecutorService`.
*/
public void exampleWithScheduledExecutorService() {
TheJob theJob = new TheJob() {
private final ScheduledExecutorService executor =
Executors.newScheduledThreadPool(1);
@Override
public void schedule(long delay) {
executor.schedule(this, delay, TimeUnit.MILLISECONDS);
}
};
theJob.schedule(1500);
}
/**
* Example with `Handler` and using already existing `Thread` with
* `Looper` (most probably the main looper).
*/
public void exampleWithHandlerAndMainLooper() {
TheJob theJob = new TheJob() {
private final Handler handler =
// new Handler();
// or if you are not in the main thread:
new Handler(Looper.getMainLooper());
@Override
public void schedule(long delay) {
handler.postDelayed(this, delay);
}
};
theJob.schedule(1500);
}
/**
* Example with `Handler` and `HandlerThread` (a convenience thread
* class with looper).
*/
public void exampleWithHandlerAndHandlerThreadsLooper() {
TheJob theJob = new TheJob() {
private final HandlerThread handlerThread;
private final Handler handler;
private final long killThreadAt;
{
handlerThread = new HandlerThread("myThread");
// handler thread must be quit when you no longer use it.
// see nextTime() method below.
killThreadAt = System.currentTimeMillis() + 30000;
// alternatively you can set it to be a daemon thread.
// handlerThread.setDaemon(true);
handlerThread.start();
handler = new Handler(handlerThread.getLooper());
}
@Override
public void schedule(long delay) {
handler.postDelayed(this, delay);
}
@Override
long nextTime() {
long nextTime = super.nextTime();
if(nextTime() > killThreadAt) {
handlerThread.quit();
}
return nextTime;
}
};
theJob.schedule(1500);
}
}
答案 2 :(得分:0)
我有一些类似的问题..我试图以不同的速度安排不同的工作,我发现使用Quartz Scheduler库来处理我的所有调度问题真正解决:)
对于您的问题:以非均匀费率开展工作,您可以轻松实施TriggerListener
并在完成时重新安排nextTime()
Quartz Scheduler可以轻松地与Spring,Maven集成,并且可以处理各种情况,例如失败的作业或线程异常。
简单示例(来自文档)
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler sched = sf.getScheduler();
// define the job and tie it to our HelloJob class
JobDetail job = newJob(HelloJob.class)
.withIdentity("job1", "group1")
.build();
// compute a time that is on the next round minute
int minutesInterval = nextTime();
// Trigger the job to run on the next round minute and repeat it forever
Trigger trigger = newTrigger()
.withIdentity("trigger1", "group1")
.withSchedule(
simpleSchedule()
.withIntervalInMinutes(minutesInterval)
.repeatForever()
)
.build();
// Tell quartz to schedule the job using our trigger
sched.scheduleJob(job, trigger);
sched.start();