我想编写一个JavaFX GUI来加载和解析更大的文件(22M),这需要大约30秒。但是,当我在任务中开始解析时,GUI的其余部分反应迟钝。然而,它并没有完全没有反应,但它仍然会持续几秒钟。为了证明这一点,我编写了一个小应用程序,它在进度条中显示解析的进度,并且并行执行另一个任务,每秒只更新一个标签。现在,第二个任务不应该被第一个任务阻止,所以我可能做错了。这是代码:
每秒都要勾选的任务:
class TimeTask extends Task<Void> {
@Override
protected Void call() throws Exception {
while (true) {
updateMessage(Instant.now().toString());
Thread.sleep(1000);
}
}
}
用于解析日志文件的替换: 从解析器以100ms的步长调用update方法。
public class ParseLogfileTask extends Task<Void> {
private LinkedList<Integer> numbers = new LinkedList<Integer>();
private Random random = new Random();
@Override
protected Void call() {
Instant startInstant = Instant.now();
while (Duration.between(startInstant, Instant.now()).getSeconds() < 25) {
for (int i = 0; i < 10000000; i++) {
numbers.add((Integer) random.nextInt());
}
Collections.shuffle(numbers);
Collections.sort(numbers);
numbers.clear();
updateParseProgress(
(int) Duration.between(startInstant, Instant.now())
.toMillis(), 25000);
}
return null;
}
public void updateParseProgress(int currentIndex, int maxIndex) {
updateProgress(currentIndex, maxIndex);
}
}
启动应用程序的方法:
@Override
public void start(Stage primaryStage) {
try {
Group root = new Group();
Scene scene = new Scene(root, 400, 400);
VBox vbox = new VBox();
vbox.setAlignment(Pos.CENTER);
ProgressBar bar = new ProgressBar();
Label label = new Label("new");
vbox.getChildren().addAll(bar, label);
root.getChildren().add(vbox);
primaryStage.setScene(scene);
primaryStage.show();
Task<Void> timeTask = new TimeTask();
Task<Void> logFileTask = new ParseLogfileTask();
bar.progressProperty().bind(logFileTask.progressProperty());
label.textProperty().bind(timeTask.messageProperty());
Platform.runLater(() -> {
new Thread(timeTask).start();
new Thread(logFileTask).start();
});
}
catch (Exception e) {
e.printStackTrace();
}
}
编辑:我在timeTask开始后删除了5秒的睡眠时间。这不是真正的问题。真正的问题是,只要logfileTask正在运行,timeTask就不会每秒更新标签。仅每1-5秒更新一次。只要logfileTask完成,timeTask就会完全按照应有的方式更新标签。这可能与Thread优先级有关吗? logfileTask产生相当大的负载...
编辑2:我无法通过其他方式来创建负载来重现问题。我只有解析器才有这些问题,解析器由ANTLR创建并创建一个巨大的解析树(许多对象)。但是,由于解析器是在后台线程中启动的,它如何干扰JavaFX Application线程?
编辑3:看起来确实是垃圾收集。我替换了日志文件解析,生成了一个巨大的数字列表,对它们进行排序并将它们再次丢弃,我可以看到与运行解析器时相同的间隙(不常见,但影响较大)
答案 0 :(得分:1)
您在 ParseLogfileTask 中调用的操作数量很多。这是一段时间(真实)。 在返回null之前使用Thread.sleep。
@Override
protected Void call() {
Instant startInstant = Instant.now();
while (Duration.between(startInstant, Instant.now()).getSeconds() < 25) {
for (int i = 0; i < 100000; i++) {
numbers.add((Integer) random.nextInt());
}
Collections.shuffle(numbers);
Collections.sort(numbers);
numbers.clear();
updateParseProgress(
(int) Duration.between(startInstant, Instant.now())
.toMillis(), 25000);
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
}
}
return null;
}
所以它对我有用。
此外,最好使用
ExecutorService service = Executors.newFixedThreadPool(2);
service.execute(timeTask);
service.execute(logFileTask);
用于调用你的主题。