不知道我是否完全理解,但我認爲這可能有幫助。
使用Platform.runLater(...)是一個適當的方法。
避免淹沒FX應用程序線程的技巧是使用Atomic變量來存儲您感興趣的值。在Platform.runLater(...)方法中,檢索它並將其設置爲sentinel值。在你的後臺線程中,更新Atomic變量,但是如果它已經被設置回它的sentinel值,則只發佈一個新的Platform.runLater(...)。
我通過查看source code for Task來解決這個問題。看看updateMessage(..)方法(在編寫本文時爲第1131行)是如何實現的。
下面是使用相同技術的示例。這只是一個(繁忙)後臺線程,它可以計算儘可能快的速度,更新一個IntegerProperty。觀察者觀察該屬性並用新值更新AtomicInteger。如果AtomicInteger的當前值是-1,它將調度一個Platform.runLater()。
在Platform.runLater中,我檢索AtomicInteger的值並使用它來更新Label,並在該過程中將該值設置回-1。這表示我已準備好進行其他UI更新。
import java.text.NumberFormat;
import java.util.concurrent.atomic.AtomicInteger;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class ConcurrentModel extends Application {
@Override
public void start(Stage primaryStage) {
final AtomicInteger count = new AtomicInteger(-1);
final AnchorPane root = new AnchorPane();
final Label label = new Label();
final Model model = new Model();
final NumberFormat formatter = NumberFormat.getIntegerInstance();
formatter.setGroupingUsed(true);
model.intProperty().addListener(new ChangeListener<Number>() {
@Override
public void changed(final ObservableValue<? extends Number> observable,
final Number oldValue, final Number newValue) {
if (count.getAndSet(newValue.intValue()) == -1) {
Platform.runLater(new Runnable() {
@Override
public void run() {
long value = count.getAndSet(-1);
label.setText(formatter.format(value));
}
});
}
}
});
final Button startButton = new Button("Start");
startButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
model.start();
}
});
AnchorPane.setTopAnchor(label, 10.0);
AnchorPane.setLeftAnchor(label, 10.0);
AnchorPane.setBottomAnchor(startButton, 10.0);
AnchorPane.setLeftAnchor(startButton, 10.0);
root.getChildren().addAll(label, startButton);
Scene scene = new Scene(root, 100, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
public class Model extends Thread {
private IntegerProperty intProperty;
public Model() {
intProperty = new SimpleIntegerProperty(this, "int", 0);
setDaemon(true);
}
public int getInt() {
return intProperty.get();
}
public IntegerProperty intProperty() {
return intProperty;
}
@Override
public void run() {
while (true) {
intProperty.set(intProperty.get() + 1);
}
}
}
}
如果你真的想「驅動器」,從UI後端:即油門後端實現,所以你看到的所有更新的速度,考慮使用AnimationTimer
。 AnimationTimer
有一個handle(...)
,每幀渲染一次。因此,您可以阻止後端實現(例如,通過使用阻塞隊列),並在每次調用句柄方法時釋放一次。在FX應用程序線程上調用handle(...)
方法。
handle(...)
方法需要一個參數,它是一個時間戳(以納秒爲單位),因此您可以使用它來進一步減慢更新速度,如果每幀一次太快。
例如:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
final BlockingQueue<String> messageQueue = new ArrayBlockingQueue<>(1);
TextArea console = new TextArea();
Button startButton = new Button("Start");
startButton.setOnAction(event -> {
MessageProducer producer = new MessageProducer(messageQueue);
Thread t = new Thread(producer);
t.setDaemon(true);
t.start();
});
final LongProperty lastUpdate = new SimpleLongProperty();
final long minUpdateInterval = 0 ; // nanoseconds. Set to higher number to slow output.
AnimationTimer timer = new AnimationTimer() {
@Override
public void handle(long now) {
if (now - lastUpdate.get() > minUpdateInterval) {
final String message = messageQueue.poll();
if (message != null) {
console.appendText("\n" + message);
}
lastUpdate.set(now);
}
}
};
timer.start();
HBox controls = new HBox(5, startButton);
controls.setPadding(new Insets(10));
controls.setAlignment(Pos.CENTER);
BorderPane root = new BorderPane(console, null, null, controls, null);
Scene scene = new Scene(root,600,400);
primaryStage.setScene(scene);
primaryStage.show();
}
private static class MessageProducer implements Runnable {
private final BlockingQueue<String> messageQueue ;
public MessageProducer(BlockingQueue<String> messageQueue) {
this.messageQueue = messageQueue ;
}
@Override
public void run() {
long messageCount = 0 ;
try {
while (true) {
final String message = "Message " + (++messageCount);
messageQueue.put(message);
}
} catch (InterruptedException exc) {
System.out.println("Message producer interrupted: exiting.");
}
}
}
public static void main(String[] args) {
launch(args);
}
}
相關問題:([登錄消息經由螺紋的JavaFX的TextArea最有效的方法] http://stackoverflow.com/questions/24116858/most-efficient-way-to-log-messages-to-javafx -textarea-via-threads-with-simple-cu) – jewelsea