需要澄清更改javafx应用程序线程中的数据吗
我一直在将我的一个项目迁移到JavaFX,并开始遇到线程问题。我将附上一个简短的例子。经过多次搜索,我终于解决了这个问题。我无法在fx应用程序线程之外更改tableView数据。我将代码从使用SwingWorker转换为任务 起初,在我将更改侦听器添加到表的observableList之前,这是有效的。然后我收到错误“不在FX应用程序线程上 当我试图更新标签的值时,onChanged方法内部发生了错误。我通过将其包装到Platform.runLater()中解决了这个问题 我只是不明白为什么更改标签会说它不在应用程序线程上。这是在什么线程上运行的?另外,我是否通过使用任务正确地将行添加到表中?在我的实际应用程序中,我可能会添加50k行,因此为什么要使用单独的线程,以免锁定UI需要澄清更改javafx应用程序线程中的数据吗,java,multithreading,javafx,Java,Multithreading,Javafx,我一直在将我的一个项目迁移到JavaFX,并开始遇到线程问题。我将附上一个简短的例子。经过多次搜索,我终于解决了这个问题。我无法在fx应用程序线程之外更改tableView数据。我将代码从使用SwingWorker转换为任务 起初,在我将更改侦听器添加到表的observableList之前,这是有效的。然后我收到错误“不在FX应用程序线程上 当我试图更新标签的值时,onChanged方法内部发生了错误。我通过将其包装到Platform.runLater()中解决了这个问题 我只是不明白为什么更改
public class Temp extends Application{
private ObservableList<String> libraryList = FXCollections.observableArrayList();
public void start(Stage stage) {
Label statusLabel = new Label("stuff goes here");
TableView<String> table = new TableView<String>(libraryList);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
TableColumn<String, String> col = new TableColumn<String, String>("Stuff");
col.setCellValueFactory(cellData -> new ReadOnlyStringWrapper(cellData.getValue()));
table.getColumns().add(col);
libraryList.addListener(new ListChangeListener<String>() {
public void onChanged(Change change) {
// Problem was caused by setting the label's text (prior to adding the runLater)
Platform.runLater(()->{
statusLabel.setText(libraryList.size()+" entries");
});
}
});
// dummy stuff
libraryList.add("foo");
libraryList.add("bar");
Button b = new Button("Press Me");
b.setOnAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent e) {
FileTask task = new FileTask();
new Thread(task).start();
}
});
BorderPane mainBody = new BorderPane();
mainBody.setTop(statusLabel);
mainBody.setCenter(table);
mainBody.setBottom(b);
Scene scene = new Scene(mainBody);
stage.setScene(scene);
stage.show();
}
class FileTask extends Task<Boolean>{
public FileTask(){
}
protected Boolean call() throws Exception{
Random rand = new Random();
for(int i = 0; i < 5; i++) {
String s = ""+rand.nextInt(Integer.MAX_VALUE);
libraryList.add(s);
}
return true;
}
}
public static void main(String[] args) {
Application.launch(args);
}
}
公共类临时扩展应用程序{
私有ObservableList libraryList=FXCollections.observableArrayList();
公众假期开始(阶段){
标签状态标签=新标签(“东西在这里”);
TableView表格=新的TableView(libraryList);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_策略);
TableColumn col=新的TableColumn(“填充”);
col.setCellValueFactory(cellData->new ReadOnlyStringWrapper(cellData.getValue());
table.getColumns().add(col);
libraryList.addListener(新ListChangeListener(){
更改后的公共作废(更改){
//问题是由设置标签的文本(在添加runLater之前)引起的
Platform.runLater(()->{
statusLabel.setText(libraryList.size()+“条目”);
});
}
});
//假东西
图书馆列表。添加(“foo”);
图书馆列表。添加(“栏”);
按钮b=新按钮(“按下我”);
b、 setOnAction(新的EventHandler(){
公共无效句柄(ActionEvent e){
FileTask任务=新建FileTask();
新线程(任务).start();
}
});
BorderPane主体=新建BorderPane();
mainBody.setTop(状态标签);
主体。设置中心(表);
主体.立根(b);
场景=新场景(主体);
舞台场景;
stage.show();
}
类FileTask扩展了任务{
公共文件任务(){
}
受保护的布尔调用()引发异常{
Random rand=新的Random();
对于(int i=0;i<5;i++){
字符串s=“”+rand.nextInt(整数最大值);
图书馆列表。添加(s);
}
返回true;
}
}
公共静态void main(字符串[]args){
应用程序启动(args);
}
}
它按预期工作,您有应用程序线程和任务线程,它们看起来像这样:
App ------\ ----------------------
Task \-label.setText() Exception
除了应用程序线程之外,您不能在任何东西上执行任何UI工作,因此添加RunLater可以执行以下操作:
App ----\ -------------/ RunLater(label.setText()) ----------
Task \-add to list/
这很有效。有几种方法可以根据您的需要进行管理:
- 如果要更新任务中的表列表,可以将RunLater调用移动到任务内部,而不是处理程序内部,这样它仍然会让您返回到应用程序线程。这样,如果您实际上在应用程序线程上,则无需在处理程序中调用RunLater
- 另一个选项是只使用将在另一个线程上运行的Task>,并返回要添加的字符串的完整列表。如果您在任务中进行网络呼叫,获取项目列表,然后在所有项目下载到表中后添加它们,则这更可能是您想要的李>
希望格式保持不变。考虑将视图所需的信息封装在单独的类中(通常称为模型)。 视图应通过侦听器或绑定来响应模型中的更改。
您可以使用一个或多个线程来更新模型:
import java.util.Random;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class Temp extends Application{
@Override
public void start(Stage stage) {
Model model = new Model();
Label statusLabel = new Label("stuff goes here");
TableView<String> table = new TableView<>(model.getLibraryList());
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
TableColumn<String, String> col = new TableColumn<>("Stuff");
col.setCellValueFactory(cellData -> new ReadOnlyStringWrapper(cellData.getValue()));
table.getColumns().add(col);
statusLabel.textProperty().bind(Bindings.concat(model.sizeProperty.asString(), " entries"));
// dummy stuff
model.add("foo"); model.add("bar");
Button b = new Button("Press Me");
b.setOnAction(e -> {
FileTask task = new FileTask(model);
new Thread(task).start();
});
BorderPane mainBody = new BorderPane();
mainBody.setTop(statusLabel);
mainBody.setCenter(table);
mainBody.setBottom(b);
Scene scene = new Scene(mainBody);
stage.setScene(scene);
stage.show();
}
class Model {
private final ObservableList<String> libraryList;
private final IntegerProperty sizeProperty;
Model(){
libraryList = FXCollections.observableArrayList();
sizeProperty = new SimpleIntegerProperty(0);
libraryList.addListener((ListChangeListener<String>) change -> {
Platform.runLater(()->sizeProperty.set(libraryList.size()));
});
}
//synchronize if you want to use multithread
void add(String string) {
Platform.runLater(()->sizeProperty.set(libraryList.add(string)));
}
ObservableList<String> getLibraryList() {
return libraryList;
}
IntegerProperty getSizeProperty() {
return sizeProperty;
}
}
class FileTask implements Runnable{
private final Model model;
public FileTask(Model model){
this.model = model;
}
@Override
public void run() {
Random rand = new Random();
for(int i = 0; i < 5; i++) {
String s = ""+rand.nextInt(Integer.MAX_VALUE);
model.add(s);
}
}
}
public static void main(String[] args) {
Application.launch(args);
}
}
import java.util.Random;
导入javafx.application.application;
导入javafx.application.Platform;
导入javafx.beans.binding.Bindings;
导入javafx.beans.property.IntegerProperty;
导入javafx.beans.property.ReadOnlyStringWrapper;
导入javafx.beans.property.SimpleIntegerProperty;
导入javafx.collections.FXCollections;
导入javafx.collections.ListChangeListener;
导入javafx.collections.ObservableList;
导入javafx.scene.scene;
导入javafx.scene.control.Button;
导入javafx.scene.control.Label;
导入javafx.scene.control.TableColumn;
导入javafx.scene.control.TableView;
导入javafx.scene.layout.BorderPane;
导入javafx.stage.stage;
公共类临时扩展应用程序{
@凌驾
公众假期开始(阶段){
模型=新模型();
标签状态标签=新标签(“东西在这里”);
TableView table=新的TableView(model.getLibraryList());
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_策略);
TableColumn col=新的TableColumn(“填充”);
col.setCellValueFactory(cellData->new ReadOnlyStringWrapper(cellData.getValue());
table.getColumns().add(col);
statusLabel.textProperty().bind(Bindings.concat(model.sizeProperty.asString(),“entries”);
//假东西
model.add(“foo”);model.add(“bar”);
按钮b=新按钮(“按下我”);
b、 设定动作(e->{
FileTask任务=新建FileTask(模型);
新线程(任务).start();
});
BorderPane主体=新建BorderPane();
mainBody.setTop(状态标签);
主体。设置中心(表);
主体.立根(b);
场景=新场景(主体);
舞台场景;
stage.show();
}
类模型{
App -----\ ------------------------------/ label.setText() ---/ add to table list-------
Task \-build list, update progress /- return final list /
import java.util.Random;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class Temp extends Application{
@Override
public void start(Stage stage) {
Model model = new Model();
Label statusLabel = new Label("stuff goes here");
TableView<String> table = new TableView<>(model.getLibraryList());
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
TableColumn<String, String> col = new TableColumn<>("Stuff");
col.setCellValueFactory(cellData -> new ReadOnlyStringWrapper(cellData.getValue()));
table.getColumns().add(col);
statusLabel.textProperty().bind(Bindings.concat(model.sizeProperty.asString(), " entries"));
// dummy stuff
model.add("foo"); model.add("bar");
Button b = new Button("Press Me");
b.setOnAction(e -> {
FileTask task = new FileTask(model);
new Thread(task).start();
});
BorderPane mainBody = new BorderPane();
mainBody.setTop(statusLabel);
mainBody.setCenter(table);
mainBody.setBottom(b);
Scene scene = new Scene(mainBody);
stage.setScene(scene);
stage.show();
}
class Model {
private final ObservableList<String> libraryList;
private final IntegerProperty sizeProperty;
Model(){
libraryList = FXCollections.observableArrayList();
sizeProperty = new SimpleIntegerProperty(0);
libraryList.addListener((ListChangeListener<String>) change -> {
Platform.runLater(()->sizeProperty.set(libraryList.size()));
});
}
//synchronize if you want to use multithread
void add(String string) {
Platform.runLater(()->sizeProperty.set(libraryList.add(string)));
}
ObservableList<String> getLibraryList() {
return libraryList;
}
IntegerProperty getSizeProperty() {
return sizeProperty;
}
}
class FileTask implements Runnable{
private final Model model;
public FileTask(Model model){
this.model = model;
}
@Override
public void run() {
Random rand = new Random();
for(int i = 0; i < 5; i++) {
String s = ""+rand.nextInt(Integer.MAX_VALUE);
model.add(s);
}
}
}
public static void main(String[] args) {
Application.launch(args);
}
}