JavaFX应用程序线程到任务通信



我一直在学习JavaFX的任务,并使用这些任务使用Platform.runLater或任务的updateValue方法等与应用程序线程通信。然而,我的Task需要知道用户何时按下GUI上的按钮,因为这可能会更改任务的updateValue方法所需返回的值。我该怎么做?我知道如何在单线程应用程序上响应按钮按下事件,但不确定如何以线程安全的方式处理它。

更新:

到目前为止,这就是我所拥有的,这是实现按钮事件的明智方式吗?

import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.PixelFormat;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.control.Button;
import java.nio.IntBuffer;
public class TaskExample extends Application {
    
    private Canvas canvas;
    private PixelWriter pixel_writer;   
    @Override
    public void start(Stage primaryStage) throws Exception {
        canvas = new Canvas(256, 256);
        pixel_writer = canvas.getGraphicsContext2D().getPixelWriter();
   
        MyTask task = new MyTask();
        task.valueProperty().addListener((c) -> {
            if(task.getValue() != null) {
                update(task.getValue());
            }
        });     
        
        Thread thread = new Thread(task);
        thread.setDaemon(true);     
        thread.start();
        
        Button button = new Button("Button 1");
        
        // On the button click event it calls the eventFired() method
        button.setOnAction((event) -> {
            task.eventFired();
        });  
        
        Pane pane = new VBox();
        pane.getChildren().addAll(canvas, button);
        
        primaryStage.setScene(new Scene(pane));
        primaryStage.show();    
        
    }
    
    public void update(IntBuffer data) {
        pixel_writer.setPixels(
            0,
            0,
            256,
            256,
            PixelFormat.getIntArgbInstance(),
            data,
            256
        );  
    }
    public static void main(String[] args) {
        launch(args);
    }
    class MyTask extends Task<IntBuffer> {
        public void eventFired() {
            System.out.println("Event fired");
        }
    
        public void update(IntBuffer data) {
            updateValue(data);
        }
    
        @Override
        protected IntBuffer call() throws InterruptedException {
            while(true) {
                for (int i=0; i<3; i++) {
                    Thread.sleep(1000);
                    IntBuffer data = IntBuffer.allocate(256*256);                   
                    for(int j=0; j<256*256; j++) {
                        switch(i) {
                            case 0: data.put(0xFF0000FF); break;
                            case 1: data.put(0xFF00FF00); break;
                            case 2: data.put(0xFFFF0000); break;
                        }
                        
                    }
                    data.rewind();                      
                    update(data);
                }
            }
            
        }
        
    }
}

我在这里要做的是思考如何重构您正在做的事情,以避免两个不同线程之间的通信。例如,不要把你正在做的事情看作是一个长期运行的任务,随着UI的进展而更新,而是把它看作是一系列单独的任务,每个任务完成后都会更新UI。ScheduledService类提供了管理这些任务的机制,并以一种干净安全的方式在它们与FX应用程序线程之间进行通信:

import java.nio.IntBuffer;
import java.util.Arrays;
import javafx.application.Application;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.control.Button;
import javafx.scene.image.PixelFormat;
import javafx.scene.image.PixelWriter;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class TaskExample extends Application {
    private Canvas canvas;
    private PixelWriter pixel_writer;   
    @Override
    public void start(Stage primaryStage) throws Exception {
        canvas = new Canvas(256, 256);
        pixel_writer = canvas.getGraphicsContext2D().getPixelWriter();

        MyService service = new MyService();
        service.setPeriod(Duration.seconds(1));
        service.valueProperty().addListener((ols, oldData, newData) -> {
            if(newData != null) {
                update(newData);
            }
        });     
        service.start();
        Button button = new Button("Button 1");

        Pane pane = new VBox();
        pane.getChildren().addAll(canvas, button);
        primaryStage.setScene(new Scene(pane));
        primaryStage.show();    
    }

    public void update(IntBuffer data) {
        pixel_writer.setPixels(
            0,
            0,
            256,
            256,
            PixelFormat.getIntArgbInstance(),
            data,
            256
        );  
    }
    public static void main(String[] args) {
        launch(args);
    }
    class MyService extends ScheduledService<IntBuffer> {
        // both instance variables accessed only on FX Application Thread:
        private final int[] colors = {0xFF0000FF, 0xFF00FF00, 0xFFFF0000} ;
        private int count = -1 ;
        @Override
        protected Task<IntBuffer> createTask() {
            // invoked on FX Application Thread
            count = (count + 1) % colors.length ;
            return new MyTask(colors[count]);
        }
    }
    class MyTask extends Task<IntBuffer> {
        private final int color ;
        MyTask(int color) {
            // invoked on FX Application Thread:
            this.color = color ;
        }
        @Override
        protected IntBuffer call() {
            // invoked on background thread:
            IntBuffer data = IntBuffer.allocate(256*256);   
            int[] a = new int[256*256];
            Arrays.fill(a, color);
            data.put(a, 0, a.length);
            data.rewind();
            return data ;
        }
    }
}

您还没有非常具体地说明UI应该如何与后台线程交互,但如果您想在按下按钮时更改服务的行为,现在您需要更改在FX应用程序线程上调用的createTask方法的行为,而不是更改已经在不同线程上运行的方法的行为。这避免了对同步的任何"低级"担忧。

例如:

class MyService extends ScheduledService<IntBuffer> {
    // all instance variables accessed only on FX Application Thread:
    private final int[][] colors = {
            {0xFF0000FF, 0xFF00FF00, 0xFFFF0000},
            {0xFF00FFFF, 0xFFFF00FF, 0xFFFFFF00}
    };
    private int count = -1 ;
    private int scheme = 0 ;
    @Override
    protected Task<IntBuffer> createTask() {
        // invoked on FX Application Thread
        count = (count + 1) % colors[scheme].length ;
        return new MyTask(colors[scheme][count]);
    }
    public void changeScheme() {
        // invoked on FX Application Thread
        scheme = (scheme + 1) % colors.length ;
    }
}

然后只是

button.setOnAction(e -> service.changeScheme());

在此处添加对service.restart();的调用将迫使更改尽快发生:

button.setOnAction(e -> {
    service.changeScheme();
    service.restart();
});

几乎总是有一种方法可以重构代码,以利用像这样的库类来避免线程之间的低级通信。

相关内容

  • 没有找到相关文章

最新更新