Task javafx

public abstract class Task<V>
extends java.util.concurrent.FutureTask<V>
implements Worker<V>, EventTarget

A fully observable implementation of a FutureTask. Tasks exposes(暴露) additional state and observable properties  useful for programming asynchronous tasks in JavaFX, as defined in the Worker interface. An implementation of Task must override the call() method. This method is invoked on the background thread. Any state which is used in this  method must be safe to read and write from a background thread. For example, manipulating a live scene graph   from  this method is unsafe and will result in runtime exceptions.
Tasks are flexible and extremely useful for the encapsulation(封装) of "work". Because Service is designed to       execute a Task, any Tasks defined by the application or library code can easily be used with a Service. Likewise( 同样地),  since Task extends from FutureTask, it is very easy and natural to use a Task with the java concurrency     Executor API. Since a Task is Runnable, you can also call it directly (by invoking the FutureTask.run() method)    from another background thread. This allows for composition of work, or pass it to a new Thread constructed and    executed manually. Finally, since you can manually create a new Thread, passing it a Runnable, it is possible to   use the following idiom:
 Thread th = new Thread(task);
 th.setDaemon(true);
 th.start();
Note that this code sets the daemon flag of the Thread to true. If you want a background thread to prevent the VM from existing after the last stage is closed, then you would want daemon to be false. However, if you want the background threads to simply terminate after all the stages are closed, then you must set daemon to true.
Although ExecutorService defines several methods which take a Runnable, you should generally limit yourself to using the execute method inherited from Executor.
As with FutureTask, a Task is a one-shot class and cannot be reused. See Service for a reusable Worker.
Because the Task is designed for use with JavaFX GUI applications, it ensures that every change to its public properties, as well as change notifications for state, errors, and for event handlers, all occur on the main JavaFX application thread. Accessing these properties from a background thread (including the call() method) will result in runtime exceptions being raised.

It is strongly encouraged that all Tasks be initialized with immutable state upon which the Task will operate. This should be done by providing a Task constructor which takes the parameters necessary for execution of the Task. Immutable state makes it easy and safe to use from any thread and ensures correctness(正确性) in the presence of multiple threads.


In Java there is no reliable way to "kill" a thread in process. However, when cancel is called on a Task, it is important that the Task stop processing. A "run-away" Task might continue processing and updating the message, text, and progress properties even after the Task has been cancelled! In Java, cancelling a Task is a cooperative(合作的) endeavor(努力). The user of the Task will request that it be cancelled, and the author of the Task must check whether is has been cancelled within the body of the call method. There are two ways this can be done. First, the Task author may check the isCancelled method, inherited from FutureTask, to see whether the Task has been cancelled. Second, if the Task implementation makes use of any blocking calls (such as NIO InterruptibleChannels or Thread.sleep) and the task is cancelled while in such a blocking call, an InterruptedException is thrown. Task implementations which have blocking calls should recognize that an interrupted thread may be the signal for a cancelled task and should double check the isCancelled method to ensure that the InterruptedException was thrown due to the cancellation of the Task.

Examples
The following set of examples demonstrate some of the most common uses of Tasks.

A Simple Loop

The first example is a simple loop that does nothing particularly useful, but demonstrates the fundamental aspects of writing a Task correctly. This example will simply loop and print to standard out on each loop iteration. When it completes, it returns the number of times it iterated.
     Task<Integer> task = new Task<Integer>() {
         @Override protected Integer call() throws Exception {
             int iterations;
             for (iterations = 0; iterations < 100000; iterations++) {
                 if (isCancelled()) {
                     break;
                 }
                 System.out.println("Iteration " + iterations);
             }
             return iterations;
         }
     }; 
First, we define what type of value is returned from this Task. In this case, we want to return the number of times we iterated, so we will specify the Task to be of type Integer by using generics. Then, within the implementation of the call method, we iterate from 0 to 100000. On each iteration, we check to see whether this Task has been cancelled. If it has been, then we break out of the loop and return the number of times we iterated. Otherwise a message is printed to the console and the iteration count increased and we continue looping.

Checking for isCancelled() in the loop body is critical, otherwise the developer may cancel the task, but the task will continue running and updating both the progress and returning the incorrect result from the end of the call method. A correct implementation of a Task will always check for cancellation.

A Simple Loop With Progress Notification

Similar to the previous example, except this time we will modify the progress of the Task in each iteration. Note that we have a choice to make in the case of cancellation. Do we want to set the progress back to -1 (indeterminate(不确定的)) when the Task is cancelled, or do we want to leave the progress where it was at? In this case, lets leave the progress alone and only update the message on cancellation, though updating the progress after cancellation is a perfectly valid choice.
     Task<Integer> task = new Task<Integer>() {
         @Override protected Integer call() throws Exception {
             int iterations;
             for (iterations = 0; iterations < 10000000; iterations++) {
                 if (isCancelled()) {
                     updateMessage("Cancelled");
                     break;
                 }
                 updateMessage("Iteration " + iterations);
                 updateProgress(iterations, 10000000);
             }
             return iterations;
         }
     };
 
As before, within the for loop we check whether the Task has been cancelled. If it has been cancelled, we will update the Task's message to indicate that it has been cancelled, and then break as before. If the Task has not been cancelled, then we will update its message to indicate the current iteration and then update the progress to indicate the current progress.

A Simple Loop With Progress Notification And Blocking Calls

This example adds to the previous examples a blocking call. Because a blocking call may thrown an InterruptedException, and because an InterruptedException may occur as a result of the Task being cancelled, we need to be sure to handle the InterruptedException and check on the cancel state.
 Task<Integer> task = new Task<Integer>() {
         @Override protected Integer call() throws Exception {
             int iterations;
             for (iterations = 0; iterations < 1000; iterations++) {
                 if (isCancelled()) {
                     updateMessage("Cancelled");
                     break;
                 }
                 updateMessage("Iteration " + iterations);
                 updateProgress(iterations, 1000);


                 // Now block the thread for a short time, but be sure
                 // to check the interrupted exception for cancellation!
                 try {
                     Thread.sleep(100);
                 } catch (InterruptedException interrupted) {
                     if (isCancelled()) {
                         updateMessage("Cancelled");
                         break;
                     }
                 }
             }
             return iterations;
         }
     };
 
Here we have added to the body of the loop a Thread.sleep call. Since this is a blocking call, I have to handle the potential InterruptedException. Within the catch block, I will check whether the Task has been cancelled, and if so, update the message accordingly and break out of the loop.

A Task Which Takes Parameters

Most Tasks require some parameters in order to do useful work. For example, a DeleteRecordTask needs the object or primary key to delete from the database. A ReadFileTask needs the URI of the file to be read. Because Tasks operate on a background thread, care must be taken to make sure the body of the call method does not read or modify any shared state. There are two techniques most useful for doing this: using final variables, and passing variables to a Task during construction.

When using a Task as an anonymous class, the most natural way to pass parameters to the Task is by using final variables. In this example, we pass to the Task the total number of times the Task should iterate.

     final int totalIterations = 9000000;
     Task<Integer> task = new Task<Integer>() {
         @Override protected Integer call() throws Exception {
             int iterations;
             for (iterations = 0; iterations < totalIterations; iterations++) {
                 if (isCancelled()) {
                     updateMessage("Cancelled");
                     break;
                 }
                 updateMessage("Iteration " + iterations);
                 updateProgress(iterations, totalIterations);
             }
             return iterations;
         }
     };
 
Since totalIterations is final, the call method can safely read it and refer to it from a background thread.
When writing Task libraries (as opposed to specific-use implementations), we need to use a different technique. In this case, I will create an IteratingTask which performs the same work as above. This time, since the IteratingTask is defined in its own file, it will need to have parameters passed to it in its constructor. These parameters are assigned to final variables.
     public class IteratingTask extends Task<Integer> {
         private final int totalIterations;


         public IteratingTask(int totalIterations) {
             this.totalIterations = totalIterations;
         }


         @Override protected Integer call() throws Exception {
             int iterations = 0;
             for (iterations = 0; iterations < totalIterations; iterations++) {
                 if (isCancelled()) {
                     updateMessage("Cancelled");
                     break;
                 }
                 updateMessage("Iteration " + iterations);
                 updateProgress(iterations, totalIterations);
             }
             return iterations;
         }
     }
 
And then when used:
IteratingTask task = new IteratingTask(8000000);
 
In this way, parameters are passed to the IteratingTask in a safe manner, and again, are final. Thus, the call method can safely read this state from a background thread.

WARNING: Do not pass mutable state to a Task and then operate on it from a background thread. Doing so may introduce race conditions. In particular, suppose you had a SaveCustomerTask which took a Customer in its constructor. Although the SaveCustomerTask may have a final reference to the Customer, if the Customer object is mutable, then it is possible that both the SaveCustomerTask and some other application code will be reading or modifying the state of the Customer from different threads. Be very careful in such cases, that while a mutable object such as this Customer is being used from a background thread, that it is not being used also from another thread. In particular, if the background thread is reading data from the database and updating the Customer object, and the Customer object is bound to scene graph nodes (such as UI controls), then there could be a violation( 违反) of threading rules! For such cases, modify the Customer object from the FX Application Thread rather than from the background thread.
     public class UpdateCustomerTask extends Task<Customer> {
         private final Customer customer;


         public UpdateCustomerTask(Customer customer) {
             this.customer = customer;
         }


         @Override protected Customer call() throws Exception {
             // pseudo-code:
             //   query the database
             //   read the values


             // Now update the customer
             Platform.runLater(new Runnable() {
                 @Override public void run() {
                     customer.setF setFirstName(rs.getString("FirstName"));
                     // etc
                 }
             });


             return customer;
         }
     }

A Task Which Returns No Value

Many, if not most, Tasks should return a value upon completion. For CRUD Tasks, one would expect that a "Create" Task would return the newly created object or primary key, a "Read" Task would return the read object, an "Update" task would return the number of records updated, and a "Delete" task would return the number of records deleted.

However sometimes there just isn't anything truly useful to return. For example, I might have a Task which writes to a file. Task has built into it a mechanism for indicating whether it has succeeded or failed along with the number of bytes written (the progress), and thus there is nothing really for me to return. In such a case, you can use the Void type. This is a special type in the Java language which can only be assigned the value of null. You would use it as follows:
     final String filePath = "/foo.txt";
     final String contents = "Some contents";
     Task<Void> task = new Task<Void>() {
         @Override protected Void call() throws Exception {
             File file = new File(filePath);
             FileOutputStream out = new FileOutputStream(file);
             // ... and other code to write the contents ...


             // Return null at the end of a Task of type Void
             return null;
         }
     }; 

A Task Which Returns An ObservableList

Because the ListView, TableView, and other UI controls and scene graph nodes make use of ObservableList, it is common to want to create and return an ObservableList from a Task. When you do not care to display intermediate values, the easiest way to correctly write such a Task is simply to construct an ObservableList within the call method, and then return it at the conclusion of the Task.
     Task<ObservableList<Rectangle>> task = new Task<ObservableList<Rectangle>>() {
         @Override protected ObservableList<Rectangle> call() throws Exception {
             updateMessage("Creating Rectangles");
             ObservableList<Rectangle> results = FXCollections.observableArrayList();
             for (int i=0; i<100; i++) {
                 if (isCancelled()) break;
                 Rectangle r = new Rectangle(10, 10);
                 r.setX(10 * i);
                 results.add(r);
                 updateProgress(i, 100);
             }
             return results;
         }
     };
 
In the above example, we are going to create 100 rectangles and return them from this task. An ObservableList is created within the call method, populated, and then returned.

A Task Which Returns Partial Results

Sometimes you want to create a Task which will return partial results. Perhaps you are building a complex scene graph and want to show the scene graph as it is being constructed. Or perhaps you are reading a large amount of data over the network and want to display the entries in a TableView as the data is arriving. In such cases, there is some shared state available both to the FX Application Thread and the background thread. Great care must be taken to never update shared state from any thread other than the FX Application Thread.

The easiest way to do this is to expose a new property on the Task which will represent the partial result. Then make sure to use Platform.runLater when adding new items to the partial result.
     public class PartialResultsTask extends Task<ObservableList<Rectangle>> {
         // Uses Java 7 diamond operator
         private ReadOnlyObjectWrapper> partialResults =
                 new ReadOnlyObjectWrapper<>(this, "partialResults",
                         FXCollections.observableArrayList(new ArrayList()));


         public final ObservableList getPartialResults() { return partialResults.get(); }
         public final ReadOnlyObjectProperty> partialResultsProperty() {
             return partialResults.getReadOnlyProperty();
         }


         @Override protected ObservableList call() throws Exception {
             updateMessage("Creating Rectangles...");
             for (int i=0; i<100; i++) {
                 if (isCancelled()) break;
                 final Rectangle r = new Rectangle(10, 10);
                 r.setX(10 * i);
                 Platform.runLater(new Runnable() {
                     @Override public void run() {
                         partialResults.get().add(r);
                     }
                 });
                 updateProgress(i, 100);
             }
             return partialResults.get();
         }
     }

A Task Which Modifies The Scene Graph

Generally, Tasks should not interact directly with the UI. Doing so creates a tight coupling between a specific Task implementation and a specific part of your UI. However, when you do want to create such a coupling, you must ensure that you use Platform.runLater so that any modifications of the scene graph occur on the FX Application Thread.
     final Group group = new Group();
     Task<Void> task = new Task<Void>() {
         @Override protected Void call() throws Exception {
             for (int i=0; i<100; i++) {
                 if (isCancelled()) break;
                 final Rectangle r = new Rectangle(10, 10);
                 r.setX(10 * i);
                 Platform.runLater(new Runnable() {
                     @Override public void run() {
                         group.getChildren().add(r);
                     }
                 });
             }
             return null;
         }
     }; 

Reacting To State Changes Generically

Sometimes you may want to write a Task which updates its progress, message, text, or in some other way reacts whenever a state change happens on the Task. For example, you may want to change the status message on the Task on Failure, Success, Running, or Cancelled state changes.
     Task<Integer> task = new Task<Integer>() {
         @Override protected Integer call() throws Exception {
             int iterations = 0;
             for (iterations = 0; iterations < 100000; iterations++) {
                 if (isCancelled()) {
                     break;
                 }
                 System.out.println("Iteration " + iterations);
             }
             return iterations;
         }


         @Override protected void succeeded() {
             super.succeeded();
             updateMessage("Done!");
         }


         @Override protected void cancelled() {
             super.cancelled();
             updateMessage("Cancelled!");
         }


         @Override protected void failed() {
             super.failed();
             updateMessage("Failed!");
         }
     };
智源软件转载
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值