Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/388.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在gui线程中触发异步事件_Java_Events_Javafx_Concurrency_Javafx 8 - Fatal编程技术网

Java 在gui线程中触发异步事件

Java 在gui线程中触发异步事件,java,events,javafx,concurrency,javafx-8,Java,Events,Javafx,Concurrency,Javafx 8,TL;DR I正在寻找一种方法,让一个线程在另一个线程中引发事件 编辑:我说的是“立即”一词,正如一些评论者指出的那样,这是不可能的。我的意思是,如果gui线程处于空闲状态(如果我的工作做得对的话,应该是空闲的),那么它应该在低毫秒到纳秒的范围内合理地快速发生 案例示例: 我有一个有父类的项目。该父类创建一个子线程“Gui”,其中包含javafx应用程序并实现Runnable。父级和Gui都有对同一BlockingQueue的引用 我希望发生什么: 我希望能够将对象从父类发送到Gui线程,并让G

TL;DR I正在寻找一种方法,让一个线程在另一个线程中引发事件

编辑:我说的是“立即”一词,正如一些评论者指出的那样,这是不可能的。我的意思是,如果gui线程处于空闲状态(如果我的工作做得对的话,应该是空闲的),那么它应该在低毫秒到纳秒的范围内合理地快速发生

案例示例: 我有一个有父类的项目。该父类创建一个子线程“Gui”,其中包含javafx应用程序并实现Runnable。父级和Gui都有对同一BlockingQueue的引用

我希望发生什么: 我希望能够将对象从父类发送到Gui线程,并让Gui接收某种类型的事件,该事件立即调用某种类型的处理函数,这样我就可以从队列中获取一个或多个对象并将它们添加到Gui中

“观察者模式”的其他解决方案通常涉及坐在while循环中的观察者,检查一些同步队列中的新数据。这对我的应用程序不起作用,因为Javafx要求只从gui线程修改gui元素,并且gui线程必须大部分处于空闲状态,以便它有时间重新绘制内容并响应用户事件。循环将导致应用程序挂起

我发现一个可能的想法是从父线程中断Gui线程,并触发某种事件,但我找不到任何方法来实现


有什么想法吗?这种情况下的最佳实践是什么?

阅读此处的问题和答案:()


阅读以下问答:()

以上是使用
BlockingQueue
的问答


这里的教程和理论:

阅读这里的问题和答案:()


阅读以下问答:()

以上是使用
BlockingQueue
的问答


这里的教程和理论:

实际上,这里听起来您需要的只是通过
平台调用FX应用程序线程上的UI更新。runLater(…)
。这将安排一个更新,该更新将在FX应用程序线程有时间时立即执行,这将非常快,只要您没有用太多的请求淹没它。下次出现渲染脉冲时,用户将看到更新(因此从用户的角度来看,这将尽快发生)

下面是一个最简单的例子:生成数据的异步类直接在UI上调度更新

首先是一个简单的类来保存一些数据。我添加了一些功能来检查数据的“年龄”,即调用构造函数后的时间:

MyDataClass.java

public class MyDataClass {

    private final int value ;

    private final long generationTime ;

    public MyDataClass(int value) {
        this.value = value ;
        this.generationTime = System.nanoTime() ;
    }

    public int getValue() {
        return value ;
    }

    public long age() {
        return System.nanoTime() - generationTime ;
    }
}
下面是一个简单的UI,显示它接收到的所有数据,以及数据的“年龄”和所有数据的平均值:

import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.Parent;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;

public class UI {

    private final TextArea textArea ;
    private final Parent view ;
    private long total ;
    private long count ;
    private final DoubleProperty average = new SimpleDoubleProperty(0);


    public UI() {
        textArea = new TextArea();

        Label aveLabel = new Label();
        aveLabel.textProperty().bind(average.asString("Average: %.3f"));

        view = new BorderPane(textArea, null, null, aveLabel, null);
    }

    public void registerData(MyDataClass data) {
        textArea.appendText(String.format("Data: %d (received %.3f milliseconds after generation)%n", 
                data.getValue(), data.age()/1_000_000.0)); 
        count++;
        total+=data.getValue();
        average.set(1.0*total / count);
    }

    public Parent getView() {
        return view ;
    }
}
这里有一个类,它(异步)休眠很多,并生成随机数据(有点像我的实习生…)。目前,它只引用了UI,因此可以直接安排更新:

import java.util.Random;

import javafx.application.Platform;

public class DataProducer extends Thread {

    private final UI ui ;

    public DataProducer(UI ui) {
        this.ui = ui ;
        setDaemon(true);
    }

    @Override
    public void run()  {
        Random rng = new Random();
        try {
            while (true) {
                MyDataClass data = new MyDataClass(rng.nextInt(100));
                Platform.runLater(() -> ui.registerData(data));
                Thread.sleep(rng.nextInt(1000) + 250);
            } 
        } catch (InterruptedException e) {
            // Ignore and allow thread to exit
        }
    }
}
最后是应用程序代码:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class AsyncExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        UI ui = new UI();
        DataProducer producer = new DataProducer(ui);
        producer.start();
        Scene scene = new Scene(ui.getView(), 600, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}
运行此命令时,我看到UI在生成数据后大约0.1毫秒左右处理数据,这符合您的要求。(前一两个需要更长的时间,因为它们是在start方法完成之前和UI实际显示之前生成的,所以它们对
平台的调用。runLater(…)
需要等待该工作完成。)


当然,这段代码的问题是,
DataProducer
与UI和JavaFX紧密耦合(直接使用
平台
类)。您可以通过为其提供处理数据的通用使用者来移除此耦合:

import java.util.Random;
import java.util.function.Consumer;

public class DataProducer extends Thread {

    private final Consumer<MyDataClass> dataConsumer ;

    public DataProducer(Consumer<MyDataClass> dataConsumer) {
        this.dataConsumer = dataConsumer ;
        setDaemon(true);
    }

    @Override
    public void run()  {
        Random rng = new Random();
        try {
            while (true) {
                MyDataClass data = new MyDataClass(rng.nextInt(100));
                dataConsumer.accept(data);
                Thread.sleep(rng.nextInt(1000) + 250);
            } 
        } catch (InterruptedException e) {
            // Ignore and allow thread to exit
        }
    }
}
请注意,此处设置
使用者
与提供事件处理程序非常相似:每当生成数据元素时,使用者都会被“通知”或“触发”。如果希望通知多个不同的视图,您可以轻松地将其扩展为一个
列表
,并将消费者添加/删除到该列表中。数据类型
MyDataClass
扮演着事件对象的角色:它包含确切发生了什么的信息
Consumer
是一个通用函数接口,因此它可以由您选择的任何类或lambda表达式实现(正如我们在本例中所做的)

作为此版本的一个变体,您可以通过将
Platform.runLater(…)
抽象为
java.util.concurrent.Executor
(这只是运行
Runnable
s的东西),将
Platform.runLater(…)
使用者的执行分离开来:


分离类的另一种方法是使用
阻塞队列
传输数据。这有一个特性,您可以限制队列的大小,因此如果有太多的数据挂起,数据生成线程将阻塞。此外,您可以在UI类中“批量处理”许多数据更新,如果生成这些更新的速度足以让FX应用程序线程充斥太多更新,这将非常有用(我在这里不显示这些代码;您需要在a
AnimationTimer
中使用数据,并进一步放松“立即”的概念)。此版本看起来像:

import java.util.Random;
import java.util.concurrent.BlockingQueue;

public class DataProducer extends Thread {

    private final BlockingQueue<MyDataClass> queue ;

    public DataProducer(BlockingQueue<MyDataClass> queue) {
        this.queue = queue ;
        setDaemon(true);
    }

    @Override
    public void run()  {
        Random rng = new Random();
        try {
            while (true) {
                MyDataClass data = new MyDataClass(rng.nextInt(100));
                queue.put(data);
                Thread.sleep(rng.nextInt(1000) + 250);
            } 
        } catch (InterruptedException e) {
            // Ignore and allow thread to exit
        }
    }
}
然后你就这么做了

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class AsyncExample extends Application {

    private final int MAX_QUEUE_SIZE = 10 ;

    @Override
    public void start(Stage primaryStage) {

        BlockingQueue<MyDataClass> queue = new ArrayBlockingQueue<>(MAX_QUEUE_SIZE);
        UI ui = new UI(queue);
        DataProducer producer = new DataProducer(queue);
        producer.start();
        Scene scene = new Scene(ui.getView(), 600, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}
import java.util.concurrent.ArrayBlockingQueue;
导入java.util.concurrent.BlockingQueue;
导入javafx.application.application;
导入javafx.scene.scene;
导入javafx.stage.stage;
公共类异步示例扩展应用程序{
私有最终整数最大队列大小=10;
@凌驾
公共无效开始(阶段primaryStage){
BlockingQueue=新阵列BlockingQueue(最大队列大小);
UI=新UI(队列);
数据生产者=新的数据生产者(队列);
producer.start();
Sc
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class AsyncExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        UI ui = new UI();
        DataProducer producer = new DataProducer(ui::registerData, Platform::runLater);
        producer.start();
        Scene scene = new Scene(ui.getView(), 600, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}
import java.util.Random;
import java.util.concurrent.BlockingQueue;

public class DataProducer extends Thread {

    private final BlockingQueue<MyDataClass> queue ;

    public DataProducer(BlockingQueue<MyDataClass> queue) {
        this.queue = queue ;
        setDaemon(true);
    }

    @Override
    public void run()  {
        Random rng = new Random();
        try {
            while (true) {
                MyDataClass data = new MyDataClass(rng.nextInt(100));
                queue.put(data);
                Thread.sleep(rng.nextInt(1000) + 250);
            } 
        } catch (InterruptedException e) {
            // Ignore and allow thread to exit
        }
    }
}
import java.util.concurrent.BlockingQueue;

import javafx.application.Platform;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.Parent;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.layout.BorderPane;

public class UI {

    private final TextArea textArea ;
    private final Parent view ;
    private long total ;
    private long count ;
    private final DoubleProperty average = new SimpleDoubleProperty(0);


    public UI(BlockingQueue<MyDataClass> queue) {
        textArea = new TextArea();

        Label aveLabel = new Label();
        aveLabel.textProperty().bind(average.asString("Average: %.3f"));

        view = new BorderPane(textArea, null, null, aveLabel, null);

        // thread to take items from the queue and process them:

        Thread queueConsumer = new Thread(() -> {
            while (true) {
                try {
                    MyDataClass data = queue.take();
                    Platform.runLater(() -> registerData(data));
                } catch (InterruptedException exc) {
                    // ignore and let thread exit
                }
            }
        });
        queueConsumer.setDaemon(true);
        queueConsumer.start();
    }

    public void registerData(MyDataClass data) {
        textArea.appendText(String.format("Data: %d (received %.3f milliseconds after generation)%n", 
                data.getValue(), data.age()/1_000_000.0)); 
        count++;
        total+=data.getValue();
        average.set(1.0*total / count);
    }

    public Parent getView() {
        return view ;
    }
}
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class AsyncExample extends Application {

    private final int MAX_QUEUE_SIZE = 10 ;

    @Override
    public void start(Stage primaryStage) {

        BlockingQueue<MyDataClass> queue = new ArrayBlockingQueue<>(MAX_QUEUE_SIZE);
        UI ui = new UI(queue);
        DataProducer producer = new DataProducer(queue);
        producer.start();
        Scene scene = new Scene(ui.getView(), 600, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}