简体   繁体   English

从Handler迁移到ScheduledExecutorService进行调度

[英]migrating from Handler to ScheduledExecutorService for scheduling

My goal is to schedule a recurrent job that happens on a non-even rate. 我的目标是安排一项周期性工作,发生率不均匀。 I am going to migrate from first snippet to the second: 我将从第一个片段迁移到第二个片段:

1st: 第一名:

Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        if (msg.what == MSG1) {               
            //recurrent job here              
            long nextTime = nextTime();
            sendMessageAtTime(obtainMessage(MSG1), nextTime);
            }
        }
    }
};

2nd: 第二:

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);
}

My questions are: 我的问题是:

1- is it true in the first snippet that the thread, to which the mHandler is referring, is free between jobs to do other tasks or handle other messages? 1-在第一个代码段中,mHandler引用的线程在作业之间可以自由执行其他任务或处理其他消息吗?

2- However in the second snippet, Thread is always busy doing the loop. 2-但是,在第二个片段中,线程总是忙于执行循环。 right? 对?

3- How can I rewrite the second code so that I won't loose thread activity between jobs (in delays)? 3-如何重写第二个代码,以使我不会丢失作业之间的线程活动(延迟)?

Any help is highly appreciated 任何帮助都受到高度赞赏

Your second code won't work as expected. 您的第二个代码将无法正常工作。 After the first task has been scheduled and is waiting to be executed, the while loop continues to schedule more tasks, all of them with the same delay. 在安排了第一个任务并等待执行之后, while循环将继续安排更多任务,所有任务都具有相同的延迟。 So you'll end up having thousands, probably millions of tasks. 因此,您最终将拥有成千上万,甚至数百万个任务。 And of course, because the main thread is running an infinite loop without any wait, it is busy all the time. 当然,由于主线程正在运行无限循环而没有任何等待,因此它一直很忙。 This is probably not what you want. 这可能不是您想要的。

You should better use a simliar approach than the handler uses above: 您应该比上面的处理程序更好地使用类似的方法:

final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
executor.schedule(() -> {
    // do work
    // reschedule
    executor.schedule(this, nextTime() - System.currentTimeMillis());
}, delay, TimeUnit.MILLISECONDS);

(Of course you should also check that the delay you specify when rescheduling is not negative). (当然,您还应该检查重新安排时指定的延迟是否为负)。

Update: If you need to process the result of each execution individually, another approach similar to your second code example is possibly what you want. 更新:如果需要分别处理每个执行的结果,则可能需要与第二个代码示例类似的另一种方法。 It schedules the task executions insisde a loop and hands over the result to a Consumer , as soon as it is available. 它可以安排任务执行并循环执行,并将结果尽快提供给Consumer (Note the future.get() inside the loop which causes the looping thread to pause until the task is done). (请注意循环内的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());
    }
}

Also note the interruption check, so that other threads can stop execution by interrupting the looping thread. 还要注意中断检查,以便其他线程可以通过中断循环线程来停止执行。 This simplifies the usage of this method inside another task in case you want to run it on a background thread too. 如果您也想在后台线程上运行此方法,则可以简化此方法在其他任务中的使用。

Schedule could be a functional interface that provides access to the scheduling information: Schedule可以是一个功能接口,用于提供对调度信息的访问:

@FunctionalInterface
public interface Schedule {
    long nextTime();
    default TimeUnit getUnit() { return TimeUnit.MILLISECONDS; }
}

Btw.: The android.os.Handler is a very nice way to do what you want in android. 顺便说一句: android.os.Handler是在android中完成您想要的事情的一种非常不错的方法。 So you should only migrate to ScheduledExecutorService if you really need its features (eg getting a Future result). 因此,仅在确实需要它的功能(例如获取Future结果)时才应迁移到ScheduledExecutorService

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);
    }
}

I had some similar issues .. I was trying to schedule different jobs at different rates and I found using the Quartz Scheduler library to handle all my scheduling problems a real relieve :) 我遇到了一些类似的问题..我试图以不同的速度安排不同的工作,但发现使用Quartz Scheduler库来处理我所有的安排问题确实可以缓解:)

For your problem: firing a job at a non-even rate, you could easily implement a TriggerListener and on completion reschedule the same job at nextTime() 对于您的问题:以非均匀的速率触发作业,您可以轻松实现TriggerListener并在完成后在nextTime()重新计划相同的作业

The Quartz Scheduler easily integrates with Spring, Maven and has handles for all kind of scenarios like misfired jobs or thread exceptions. Quartz Scheduler可以轻松地与Spring,Maven集成,并具有适用于各种情况的句柄,例如作业失败或线程异常。

Simple example (from the docs) 简单示例(来自文档)

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();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 使用 ScheduledExecutorService 调度 Applet 启动 - Scheduling an Applet start with ScheduledExecutorService Java ScheduledExecutorService中使用的调度算法 - Scheduling algorithm used in Java ScheduledExecutorService Timer vs. ScheduledExecutorService 调度 - Timer vs. ScheduledExecutorService scheduling ScheduledExecutorService调用关闭以进行无限调度 - ScheduledExecutorService call shutdown for infinite scheduling 从Spring 3迁移到Spring 4-org.springframework.scheduling.quartz.CronTriggerBean - Migrating from Spring 3 to Spring 4 - org.springframework.scheduling.quartz.CronTriggerBean 从ScheduledExecutorService发送广播 - Sending broadcast from ScheduledExecutorService 从ScheduledExecutorService中提取异常 - Extracting exceptions from a ScheduledExecutorService 如何从 ScheduledExecutorService 中删除任务? - How to remove a task from ScheduledExecutorService? 从 ScheduledExecutorService 停止和删除任务 - Stopping and removing a task from ScheduledExecutorService 使用ScheduledExecutorService和Service中的newSingleThreadScheduledExecutor安排任务,导致任务在多个线程中运行 - Scheduling a task using ScheduledExecutorService with newSingleThreadScheduledExecutor in Service resulting in task running in multiple threads
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM