如何在一个正在运行的线程上调用一个方法,但在另一个正在运行的线程上执行它?

问题描述 投票:0回答:1

我是 Java 线程的新手。我有一个在 2 个线程上运行的客户端应用程序:

WorkerThread
JavaFxGuiThread
。我的问题是:我可以在
GuiThread
(按下按钮)上调用方法但在
WorkerThread
上执行它吗?请注意,在我调用方法并执行它时,两个线程已经在做其他事情了。 这是我的代码示例:

class WorkerThread implements Runnable {
    @Override
    public void run() {
        searchForConnections(); // <-- should run at the start of a thread
        
        // should wait for onButtonPressed() and only then execute
        // sendMessage() on WorkerThread, not on GuiThread
    }
    private String searchForConnections() {
        // constantly searches for connections
        return connection;
    }
    private void sendMessage() {
        //sends message
        System.out.println("message sent!");
    }
}
class GuiThread implements Runnable {
    @Override
    public void run() {
        runGui(); // <-- should run at the start of a thread
        
        // waits until user press the button
        // and only then:
        onButtonPressed();
    }
    private void onButtonPressed() {
        // should somehow run sendMessage() inside
        // WorkerThread run() method
        // ( which should run sendMessage() on WorkerThread not GuiThread )
    }
}
public class Main {
    public static void main(String[] args) {
        new Thread(new WorkerThread()).start();  // starts GuiThread run() method
        new Thread(new GuiThread()).start();     // starts WorkerThread run() method
    }
}

我想避免在

sendMessage()
上给
GuiThread
打电话。

这里的类似答案仅显示如何在线程开始时运行方法,而我相信我的情况有所不同,因为线程已经在运行。

java multithreading javafx java-threads
1个回答
0
投票

我相信你有一个轻微的概念误解。不要考虑如何从一个线程调用方法并让它在不同的线程上执行。相反,考虑如何在线程之间传递对象。您想给您的“工作线程”发送一条消息。因此,您需要弄清楚如何将该消息 object 传递给所述线程。

本质上,您需要提供一个“同步点”,您可以在其中在线程之间传递数据(甚至是动作,例如

Runnable
s)。有很多方法可以做到这一点。您决定如何执行此操作取决于您的这个“工作线程”应该如何工作。无论如何,我建议对 Java 并发性进行一些研究。看看:

并发很难,要精通它需要一些工作。

就是说,如果您的“工作线程”打算在用户输入时发送多条消息,那么最简单的解决方案之一可能是使用

BlockingQueue
。这是一个示例(在 JavaFX 中,给定 标记):

import java.util.Objects;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.stage.Stage;

public class Main extends Application {

  private ExecutorService executor;

  @Override
  public void start(Stage primaryStage) {
    executor = Executors.newSingleThreadExecutor();

    BlockingQueue<String> messageQueue = new ArrayBlockingQueue<>(10);
    executor.execute(new Messenger(messageQueue));

    TextField inputField = new TextField();

    Button sendBtn = new Button("Send");
    sendBtn.setDefaultButton(true);
    sendBtn.setOnAction(e -> {
      e.consume();

      String message = inputField.getText();
      if (message != null && !message.isEmpty()) {
        messageQueue.add(message);
        inputField.clear();
      }
    });

    HBox root = new HBox(10, inputField, sendBtn);
    root.setPadding(new Insets(10));
    HBox.setHgrow(inputField, Priority.ALWAYS);

    primaryStage.setScene(new Scene(root, 600, -1));
    primaryStage.setTitle("JavaFX Demo");
    primaryStage.show();
  }

  @Override
  public void stop() {
    executor.shutdownNow();
  }

  private static class Messenger implements Runnable {

    private final BlockingQueue<String> queue;

    Messenger(BlockingQueue<String> queue) {
      this.queue = Objects.requireNonNull(queue, "queue");
    }

    @Override
    public void run() {
      try {
        searchForConnections();
        while (!Thread.interrupted()) {
          String message = queue.take();
          sendMessage(message);
        }
      } catch (InterruptedException ignore) {
        // If the thread is interrupted, we simply want to stop
      }
    }

    private void searchForConnections() {}

    private void sendMessage(String message) {
      System.out.printf("Sending message: '%s'%n", message);
    }
  }
}

注意这实际上是如何工作的。我们不是从一个线程调用方法,而是让它在另一个线程上执行。我们有后台线程(你的“工作线程”)阻塞在

BlockingQueue
等待消息发送。当 JavaFX 应用程序线程(UI 线程)将一个
String
放入队列时,后台线程获取它并使用
sendMessage
调用
String

此外,如果这里有一些混淆,请注意我们如何不创建或管理 UI 线程。这是由 UI 框架处理的,并且已经在本质上运行在一个循环中,该循环处理用户生成的事件并将渲染的 UI 与场景图的状态同步。

© www.soinside.com 2019 - 2024. All rights reserved.