JavaFX有一個javafx.concurrent
API;特別是,Task
類很適合您的用例。此API旨在與java.util.concurrent
API協同工作。例如,Task
是FutureTask
的實現,所以它可以提交給Executor
。當你想使用一個線程池,您可以創建一個Executor
實現一個線程池你,並提交你的任務是:
final int MAX_THREADS = 4 ;
Executor exec = Executors.newFixedThreadPool(MAX_THREADS);
由於這些線程的UI應用程序的後臺運行,你可能不希望他們阻止應用程序退出。您可以通過你的遺囑執行人守護線程創建的線程實現這一目標:
Executor exec = Executors.newFixedThreadPool(MAX_THREADS, runnable -> {
Thread t = new Thread(runnable);
t.setDaemon(true);
return t ;
});
產生的執行將有多達MAX_THREADS
線程池。如果在沒有線程可用時提交任務,則它們將在隊列中等待,直到線程變爲可用。
實現實際Task
,有幾件事情要牢記:
您不得更新從後臺線程的用戶界面。由於您的Task
已提交給上述執行程序,因此將在後臺線程上調用call()
方法。如果在執行call
方法期間確實需要更改UI,則可以將代碼更改爲Platform.runLater(...)
中的UI,但最好進行結構組合,以避免出現這種情況。特別是,Task
有一組updateXXX(...)
方法,用於更改FX應用程序線程上相應屬性的值。您的UI元素可以根據需要綁定到這些屬性。
建議call
方法不要訪問任何共享數據(通過上面提到的updateXXX(...)
方法除外)。實例化您的Task
子類僅設置final
變量,讓call()
方法計算一個值並返回該值。
要取消Task
,Task
類定義了一個內置的cancel()
方法。如果您有長期運行的call()
方法,則應定期檢查isCancelled()
的值,並在返回true
時停止做功。
下面是一個基本的例子:
import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import javafx.application.Application;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.value.ChangeListener;
import javafx.concurrent.Task;
import javafx.concurrent.Worker;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.ProgressBarTableCell;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
public class FileTaskExample extends Application {
private static final Random RNG = new Random();
private static final int MAX_THREADS = 4 ;
private final Executor exec = Executors.newFixedThreadPool(MAX_THREADS, runnable -> {
Thread t = new Thread(runnable);
t.setDaemon(true);
return t ;
});
@Override
public void start(Stage primaryStage) {
// table to display all tasks:
TableView<FileProcessingTask> table = new TableView<>();
TableColumn<FileProcessingTask, File> fileColumn = new TableColumn<>("File");
fileColumn.setCellValueFactory(cellData -> new ReadOnlyObjectWrapper<File>(cellData.getValue().getFile()));
fileColumn.setCellFactory(col -> new TableCell<FileProcessingTask, File>() {
@Override
public void updateItem(File file, boolean empty) {
super.updateItem(file, empty);
if (empty) {
setText(null);
} else {
setText(file.getName());
}
}
});
fileColumn.setPrefWidth(200);
TableColumn<FileProcessingTask, Worker.State> statusColumn = new TableColumn<>("Status");
statusColumn.setCellValueFactory(cellData -> cellData.getValue().stateProperty());
statusColumn.setPrefWidth(100);
TableColumn<FileProcessingTask, Double> progressColumn = new TableColumn<>("Progress");
progressColumn.setCellValueFactory(cellData -> cellData.getValue().progressProperty().asObject());
progressColumn.setCellFactory(ProgressBarTableCell.forTableColumn());
progressColumn.setPrefWidth(100);
TableColumn<FileProcessingTask, Long> resultColumn = new TableColumn<>("Result");
resultColumn.setCellValueFactory(cellData -> cellData.getValue().valueProperty());
resultColumn.setPrefWidth(100);
TableColumn<FileProcessingTask, FileProcessingTask> cancelColumn = new TableColumn<>("Cancel");
cancelColumn.setCellValueFactory(cellData -> new ReadOnlyObjectWrapper<FileProcessingTask>(cellData.getValue()));
cancelColumn.setCellFactory(col -> {
TableCell<FileProcessingTask, FileProcessingTask> cell = new TableCell<>();
Button cancelButton = new Button("Cancel");
cancelButton.setOnAction(e -> cell.getItem().cancel());
// listener for disabling button if task is not running:
ChangeListener<Boolean> disableListener = (obs, wasRunning, isNowRunning) ->
cancelButton.setDisable(! isNowRunning);
cell.itemProperty().addListener((obs, oldTask, newTask) -> {
if (oldTask != null) {
oldTask.runningProperty().removeListener(disableListener);
}
if (newTask == null) {
cell.setGraphic(null);
} else {
cell.setGraphic(cancelButton);
cancelButton.setDisable(! newTask.isRunning());
newTask.runningProperty().addListener(disableListener);
}
});
return cell ;
});
cancelColumn.setPrefWidth(100);
table.getColumns().addAll(Arrays.asList(fileColumn, statusColumn, progressColumn, resultColumn, cancelColumn));
Button cancelAllButton = new Button("Cancel All");
cancelAllButton.setOnAction(e ->
table.getItems().stream().filter(Task::isRunning).forEach(Task::cancel));
Button newTasksButton = new Button("Process files");
FileChooser chooser = new FileChooser();
newTasksButton.setOnAction(e -> {
List<File> files = chooser.showOpenMultipleDialog(primaryStage);
if (files != null) {
files.stream().map(FileProcessingTask::new).peek(exec::execute).forEach(table.getItems()::add);
}
});
HBox controls = new HBox(5, newTasksButton, cancelAllButton);
controls.setAlignment(Pos.CENTER);
controls.setPadding(new Insets(10));
BorderPane root = new BorderPane(table, null, null, controls, null);
Scene scene = new Scene(root, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static class FileProcessingTask extends Task<Long> {
private final File file ;
public FileProcessingTask(File file) {
this.file = file ;
}
public File getFile() {
return file ;
}
@Override
public Long call() throws Exception {
// just to show you can return the result of the computation:
long fileLength = file.length();
// dummy processing, in real life read file and do something with it:
int delay = RNG.nextInt(50) + 50 ;
for (int i = 0 ; i < 100; i++) {
Thread.sleep(delay);
updateProgress(i, 100);
// check for cancellation and bail if cancelled:
if (isCancelled()) {
updateProgress(0, 100);
break ;
}
}
return fileLength ;
}
}
public static void main(String[] args) {
launch(args);
}
}
看看這個[樣品](https://gist.github.com/jewelsea/4947946),其使用['ExecutorService'](HTTPS: //docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html)從['Executors']獲取(https://docs.oracle.com/javase/8/docs/ api/java/util/concurrent/Executors.html)來管理多個任務,並在以下答案中進一步討論:[如何在JavaFX2中的任務之間重置進度指示器?](http://stackoverflow.com/questions/16368793/如何對復位進度指示器之間的任務 - 在-javafx2) – jewelsea