线程之间的非阻塞信号传递

huangapple go评论74阅读模式
英文:

Non-blocking signaling between threads

问题

我有一个JavaFX应用,在启动时运行两个线程。一个是UI线程,不能被阻塞。另一个是准备一个大表格的线程(大约需要20秒)。我想在第二个线程完成时通知UI线程,这样它就可以将一个矩形的颜色从红色更改为绿色。我尝试过使用synchronized关键字的解决方案,但它们都导致了UI线程被阻塞。

英文:

I have a JavaFX app that runs two threads at startup. One is the UI thread that must not be blocked. The other is a thread that prepares a large table (it takes about 20 seconds). I want to signal the UI thread when the second thread is done, so it can change the color of a rectangle from red to green. I have tried solutions using the synchronized keyword, but they all caused the UI thread to be blocked.

答案1

得分: 1

我使用了以下资源来获取下面的代码。

下面的应用程序简单地显示一个红色矩形,经过五秒钟后会变成绿色。在代码之后进行解释。

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.concurrent.Worker;
import javafx.concurrent.Worker.State;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class JfxTask0 extends Application {
    private Task<Void> task;

    @Override
    public void init() throws Exception {
        task = new Task<Void>() {
            
            @Override
            protected Void call() throws Exception {
                try {
                    Thread.sleep(5000L);
                }
                catch (InterruptedException xInterrupted) {
                    if (isCancelled()) {
                        System.out.println("CANCELLED!");
                    }
                }
                return null;
            }
        };
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        Rectangle rect = new Rectangle(25.0d, 25.0d, 50.0d, 50.0d);
        rect.setFill(Color.RED);
        task.stateProperty().addListener(new ChangeListener<Worker.State>() {

            @Override
            public void changed(ObservableValue<? extends State> workerStateProperty,
                                Worker.State oldValue,
                                Worker.State newValue) {
                if (newValue == Worker.State.SUCCEEDED) {
                    rect.setFill(Color.GREEN);
                }
            }
        });
        new Thread(task).start();
        Group root = new Group();
        ObservableList<Node> children = root.getChildren();
        children.add(rect);
        Scene scene = new Scene(root, 100.0D, 100.0D);
        primaryStage.setScene(scene);
        primaryStage.setTitle("Task");
        primaryStage.show();
    }

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

方法init()在类javafx.application.Application中声明。它在方法start()之前执行,正如其名称所示,用于初始化JavaFX应用程序。在这个方法中,我创建了后台任务。后台任务只是简单地睡眠了五秒钟。

在方法start()中,我创建了红色矩形,然后启动了后台任务,但在启动任务之前,我为任务的一个属性注册了一个监听器。一旦任务完成,该属性将被设置为特定的值。

任务启动后,我构建了剩余的GUI并显示它。

一旦任务终止,监听器就会被调用,它将矩形的颜色设置为绿色。

英文:

I used the following resources to obtain the below code.

The below app simply displays a red rectangle which, after five seconds, turns to green. Explanations after the code.

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.concurrent.Worker;
import javafx.concurrent.Worker.State;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class JfxTask0 extends Application {
    private Task&lt;Void&gt;  task;

    @Override
    public void init() throws Exception {
        task = new Task&lt;Void&gt;() {
            
            @Override
            protected Void call() throws Exception {
                try {
                    Thread.sleep(5000L);
                }
                catch (InterruptedException xInterrupted) {
                    if (isCancelled()) {
                        System.out.println(&quot;CANCELLED!&quot;);
                    }
                }
                return null;
            }
        };
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        Rectangle rect = new Rectangle(25.0d, 25.0d, 50.0d, 50.0d);
        rect.setFill(Color.RED);
        task.stateProperty().addListener(new ChangeListener&lt;Worker.State&gt;() {

            @Override
            public void changed(ObservableValue&lt;? extends State&gt; workerStateProperty,
                                Worker.State oldValue,
                                Worker.State newValue) {
                if (newValue == Worker.State.SUCCEEDED) {
                    rect.setFill(Color.GREEN);
                }
            }
        });
        new Thread(task).start();
        Group root = new Group();
        ObservableList&lt;Node&gt; children = root.getChildren();
        children.add(rect);
        Scene scene = new Scene(root, 100.0D, 100.0D);
        primaryStage.setScene(scene);
        primaryStage.setTitle(&quot;Task&quot;);
        primaryStage.show();
    }

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

Method init() is declared in class javafx.application.Application. It is executed before method start() and, as its name suggests, is used to initialize the JavaFX application. In this method I create the background task. The background task merely sleeps for five seconds.

In method start() I create the red rectangle and then launch the background task but before launching the task, I register a listener with one of the task's properties. This property will be set to a particular value once the task completes.

After the task is launched, I build the rest of the GUI and display it.

Once the task terminates, then listener is invoked and it sets the rectangle color to green.

答案2

得分: 0

你可以为这个问题使用一个处理程序。
这里有一个示例。
在你的主活动中添加以下内容并创建处理程序。

Handler h = new Handler(){
    @Override
    public void handleMessage(Message msg){
        switch(msg.what){
            case 1:
                // 在完成时执行的操作
                break;
            default:
                break;
        }
    }
};

MyThread thread = new MyThread(new Messenger(h));
thread.start();

现在在你的线程文件中添加以下内容。

public class MyThread extends Thread {
    Messenger m;
    
    public MyThread(Messenger m){
        this.m = m;
    }
    
    @Override
    public void run(){
        super.run();
        
        // 你的代码
        
        // 当任务完成时
        Message msg = Message.obtain();
        msg.what = 1;
        msg.obj = "";
        try{
            m.send(msg);
        } catch(IOException e){
            e.printStackTrace();
        }
    }
}
英文:

You can use a handler for this problem.
there is example
Add this in your main activity and create handler.

Handler h = new Handler(){
@Override public void handleMessage(Message msg){
switch(msg.what){
case 1:
// what you want when complete 
break;
default:
break;
}
}
}
MyThread thread = new MyThread(new Messenger(h));
thread.start();

Now add this in your thread file.

public class MyThread{
Messenger m;
public MyThread(Messenger m){
this.m = m;
}
public void run(){
super.run();
// your codes
//
//when your task complete 
Message msg = Message.obtain();
msg.what = 1;
msg.obj = &quot;&quot;;
try{
m.send(msg);
}catch(IOException e){
e.printStackTrace();
}
}
}

huangapple
  • 本文由 发表于 2020年9月20日 22:45:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/63980194.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定