我是 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
打电话。
这里的类似答案仅显示如何在线程开始时运行方法,而我相信我的情况有所不同,因为线程已经在运行。
我相信你有一个轻微的概念误解。不要考虑如何从一个线程调用方法并让它在不同的线程上执行。相反,考虑如何在线程之间传递对象。您想给您的“工作线程”发送一条消息。因此,您需要弄清楚如何将该消息 object 传递给所述线程。
本质上,您需要提供一个“同步点”,您可以在其中在线程之间传递数据(甚至是动作,例如
Runnable
s)。有很多方法可以做到这一点。您决定如何执行此操作取决于您的这个“工作线程”应该如何工作。无论如何,我建议对 Java 并发性进行一些研究。看看:
synchronized
关键字和相应的wait
、notify
和notifyAll
方法(都在Object
中声明)。
java.util.concurrent
、java.util.concurrent.atomic
和java.util.concurrent.locks
包。
JavaFX 中的并发性和
javafx.concurrent
包(都是 JavaFX 特定的;您包含了 javafx 标签)。
并发很难,要精通它需要一些工作。
就是说,如果您的“工作线程”打算在用户输入时发送多条消息,那么最简单的解决方案之一可能是使用
BlockingQueue
。这是一个示例(在 JavaFX 中,给定 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 与场景图的状态同步。