JavaFX的同步机制(一)

2014-11-24 11:47:27 · 作者: · 浏览: 60
为什么要使用javafx.concurrent包
代表应用程序用户界面的 JavaFX场景图不是线程安全的,并且只能通过已知的JavaFX 应用程序的UI线程来访问和修改。在JavaFX应用程序线程实现一个长时任务不可避免地导至UI不响应(假死)。最好的做法是将这些长时任务放在后台的一个或多个线程中执行,而让JavaFX应用程序线程处理用户事件。
如果通过创建一个Runnable对象和新线程(Java的同步机制)的方式实现一个后台任务,而在某些情况下又必须和JavaFX应用程序线程就线程的执行结果或执行进度进行通讯,将很容易导致错误。相反,如果使用java.concurrent包中提供的JavaFX API,则能处理好与UI进行交互的多线程代码,并且保证这种交互只发生在正确的线程里面。
Javafx.concurrent包总览
Java平台在java.util.concerrent包提供了一套完整的同步类库。Javafx.concerrrent包考虑了JavaFX应用程序线程和GUI开发人员面对的各种限制,在已有的API间作了平衡。
Javafx.concerrent包由Worker接口和Task、Service两个基础类构成,这两个基础类也实现了Worker接口。Worker接口提供了后台线程与UI进行通讯的非常有用的API,Task类是java.util.concerrent.FutureTask类遵守可观察模式的实现。Task类使得开发者能够在JavaFX应用程序中实现匿名任务,而Service类则执行任务。
WorkerStateEvent类随时确认因Work对象的状态改变而触发的事件,Task类和Service类均实现了EventTarget接口,因此支持监听状态改变事件。
Worker接口
Worker接口定义在一个或多个后台线程中执行任务的对象,Worker对象的状态是可观察的,可在JavaFX应用程序线程进行访问。
Worker对象的生命周期是这样定义的:创建后,Worker对象处于READY状态;当被安排执行是,转为SCHEDULED状态;然后当Worker对象执行任务时,状态变为RUNNING。注意即使Worker对象未经安排马上执行,它也首先转为SCHEDULED状态,然后是RUNNING状态。当Worker对象的任务顺利完成,状态为SUCCEEDED,value属性的值设置为这个Worker对象的结果。反之,如果这个Worker对象在执行过程中抛出异常,它的状态为FAILED,并且exception属性的值被设置为抛出的异常类型。在Worker对象的任务未执行完之前的任何时候,开发者都可以通过调可cance方法中断任务,这将使得Worker对象的状态转变为CANCELLED。
Worker对象执行任务的进度可以通过totalWork、workDone、progress三个不同的属性获得。关于更多参数取值范围的信息,请参照API文档。
Task类
Task类用于实现一个需要在后台线程中完成的业务逻辑。首先,需要继承Task类,Task类的实现必须重写call方法,以完成后台工作并返回执行结果。
Call方法是在后台线程中调用的,因此这个方法只能在后台线程中以安全的方式读取或写入状态。例如,从call方法中操纵一个活动的场景图将导致运行时异常。另一方面,Task类是设计用于JavaFX GUI应用程序的,它可保证任何公共属性的改变、错误或取消的通知、事件处理、状态等正确地在JavaFX应用程序线程产生。在call方法内部,可以使用updateProgress、updateMessage、updateTitle方法,它们负责更新JavaFX应用程序线程中相应的属性值。但是,如果task线程被取消,从call方法返回的值将被忽略。
注意Task类适配Java同步类库,因为它继承了java.utils.concurrent.FutureTask类,该类实现了Runnable接口。正是由于这个原因,一个Task对象可以于用Java同步中的Executor API,也可以将它作为参数传第到Java线程,这将允话另一个背景线程调用这个Task。对Java同步API较好的理解可以帮助理解JavaFX的同步机制。
Task可以用以下方法启动:
将Task作为线程参数启动:
Threadth=new Thread(task);
th.setDeamon(true);
th.start();
使用ExecutorServiceAPI
ExecutorService.submit(task);
Task类定义为一次性对象,无法重用,如果需要可重用的Worker对象,可使用Service类。
取消Task
在Java中并没有提供一种可靠的方法在进程中终止线程。然而,在Task上调用cancel时任务必须终止。因此必须在call方法内使用IsCancelled方法来定期检测任务是否被取消。例1展示了Task类检测取消状态正确的实现方法。
例1:
[java]
import javafx.concurrent.Task;
Task task = newTask() {
@Override protected Integer call() throws Exception {
int iterations;
for (iterations = 0; iterations <100000; iterations++) {
if (isCancelled()) {
break;
}
System.out.println("Iteration" + iterations);
}
return iterations;
}
};
如果任务实现通过Thread.sleep锁定,并且在锁定时取消了任务,则会抛出InterruptedException例外。对于这些任务,中断线程会对被取消任务发出信号。因此,锁定的任务必须通过isCancelled方法进行双重检测以保证InterruptedException例外能根据任务的取消状态适时抛出。如例2所示:
例2:
[java]
import javafx.concurrent.Task;
Task task = new Task() {
@Override protected Integer call() throws Exception {
int iterations;
for (iterations = 0; iterations < 1000; iterations++) {
if (isCancelled()) {
updateMessage("Cancelled");
b