调度器
有时候你需要让一些事情在未来发生,那么你在哪儿看它们呢?看看ActorSystem吧!在那儿你能找到scheduler方法,它返回一个akka.actor.Scheduler实例,这个实例每一个ActorSystem都是唯一的,在内部通用调度在特定时间点发生的事情。
你可以调度向Actor发送消息和执行任务(函数或者Runnable)。你将获得一个Cancellable,你可以调用其cancel方法来取消计划的操作的执行。
警告
Akka使用的Scheduler的默认实现是基于任务桶,根据固定的计划变空。它不再精确的时间执行任务,而是在每一个滴答时刻,它会运行到期(或过期)的所有事情。默认调度器的精确性是可以通过akka.scheduler.tick-duration配置属性进行修改。
一些示例
import akka.actor.Props;
import docs.AbstractJavaTest;
import scala.concurrent.duration.Duration;
import java.util.concurrent.TimeUnit;
system.scheduler().scheduleOnce(Duration.create(50, TimeUnit.MILLISECONDS),
testActor, "foo", system.dispatcher(), null);
调度一个Runnable
,50ms
之后执行,
它发送当前时间给testActor
:
system.scheduler().scheduleOnce(Duration.create(50, TimeUnit.MILLISECONDS),
new Runnable() {
@Override
public void run() {
testActor.tell(System.currentTimeMillis(), ActorRef.noSender());
}
}, system.dispatcher());
警告
如果你调度一个Runnable实例,你应该格外小心不要传入或关闭不稳定的引用。实际上,这意味着你不应该调用Runnable作用域内actor上的方法。如果你需要调度一个调用,最好使用能够接受消息并且ActorRef能调度消息给自己的schedule()变体(包含必要的参数),然后当收到消息时调用方法。
计划在0ms之后发送"Tick"消息给tickActor,每50ms重复一次:
import akka.actor.Props;
import docs.AbstractJavaTest;
import scala.concurrent.duration.Duration;
import java.util.concurrent.TimeUnit;
import akka.actor.UntypedActor;
import akka.actor.Cancellable;
class Ticker extends UntypedActor {
@Override
public void onReceive(Object message) {
if (message.equals("Tick")) {
// Do someting
} else {
unhandled(message);
}
}
}
ActorRef tickActor = system.actorOf(Props.create(Ticker.class,this));
//This will schedule to send the Tick-message
//to the tickActor after 0ms repeating every 50ms
Cancellable cancellable = system.scheduler().schedule(Duration.Zero(),
Duration.create(50, TimeUnit.MILLISECONDS), tickActor,"Tick",
system.dispatcher(), null);
//This cancels further Ticks to be sent
cancellable.cancel();
来自akka.actor.ActorSystem
/**
* Light-weight scheduler for running asynchronous tasks after some deadline
* in the future. Not terribly precise but cheap.
*/
def scheduler: Scheduler
警告
当ActorSystem被终止时,所有被调度的任务都将被执行,即任务在它超时之前可能会被执行。
用于实现类的Scheduler接口
实际的调度器实现是在ActorSystem启动时通过反射加载的,这意味着可以用akka.scheduler.implementation配置属性提供一个不同的实现。相关的实现必须实现下面的接口:
/**
* An Akka scheduler service. This one needs one special behavior: if
* Closeable, it MUST execute all outstanding tasks upon .close() in order
* to properly shutdown all dispatchers.
*
* Furthermore, this timer service MUST throw IllegalStateException if it
* cannot schedule a task. Once scheduled, the task MUST be executed. If
* executed upon close(), the task may execute before its timeout.
*
* Scheduler implementation are loaded reflectively at ActorSystem start-up
* with the following constructor arguments:
* 1) the system’s com.typesafe.config.Config (from system.settings.config)
* 2) a akka.event.LoggingAdapter
* 3) a java.util.concurrent.ThreadFactory
*/
public abstract class AbstractScheduler extends AbstractSchedulerBase {
/**
* Schedules a function to be run repeatedly with an initial delay and
* a frequency. E.g. if you would like the function to be run after 2
* seconds and thereafter every 100ms you would set delay = Duration(2,
* TimeUnit.SECONDS) and interval = Duration(100, TimeUnit.MILLISECONDS)
*/
@Override
public abstract Cancellable schedule(FiniteDuration initialDelay,
FiniteDuration interval, Runnable runnable, ExecutionContext executor);
/**
* Schedules a Runnable to be run once with a delay, i.e. a time period that
* has to pass before the runnable is executed.
*/
@Override
public abstract Cancellable scheduleOnce(FiniteDuration delay, Runnable runnable,
ExecutionContext executor);
/**
* The maximum supported task frequency of this scheduler, i.e. the inverse
* of the minimum time interval between executions of a recurring task, in Hz.
*/
@Override
public abstract double maxFrequency();
}
Cancellable接口
调用一个任务会返回Cancellable (或者抛出IllegalStateException,如果尝试在调度器关闭的情况下尝试)。这个对象允许取消那些已经计划要执行的事情。
警告
如果任务已经开始,那么它不会中止这个任务的执行。检查cancel方法的返回值,以检测是否计划的任务已经被取消或者已经运行起来。
/**
* Signifies something that can be cancelled
* There is no strict guarantee that the implementation is thread-safe,
* but it should be good practice to make it so.
*/
trait Cancellable {
/**
* Cancels this Cancellable and returns true if that was successful.
* If this cancellable was (concurrently) cancelled already, then this method
* will return false although isCancelled will return true.
*
* Java & Scala API
*/
def cancel(): Boolean
/**
* Returns true if and only if this Cancellable has been successfully cancelled
*
* Java & Scala API
*/
def isCancelled: Boolean
}