运行 java 具有多个 UI 线程的应用程序
Running java Application with Multiple UI Threads
我正尝试在 java 中编写一个非常简单的客户端-服务器电子邮件项目。
我已经使用套接字编写了客户端和服务器之间的通信代码,现在我正在尝试编写一些测试代码,其中还包括一个非常简单的 UI。
我的想法是创建与我拥有的客户一样多的线程,我希望每个线程都开始打开一个简单的 UI window 创建的 Java FX 但我有一些问题。
这是主要的class:
import java.io.*;
public class ClientController{
public static void main(String args[]) throws IOException {
ParallelClient c1=new ParallelClient("aaaa@gmail.com");
ParallelClient c2=new ParallelClient("bbbb@gmail.com");
c1.start();
c2.start();
}
}
这是 ParallelClient class:
import ...
public class ParallelClient extends Thread{
private String user;
public ParallelClient(String user){
this.user=user;
}
public void run(){
ClientApp app=new ClientApp();
try {
app.start(new Stage());
} catch (Exception e) {
e.printStackTrace();
}
...
}
...
}
这是设置新 window:
的 ClientApp class
import ...
public class ClientApp extends Application {
@Override
public void start(Stage stage) throws Exception {
try {
Parent root = FXMLLoader.load(getClass().getResource("ui/client-management.fxml"));
stage.setTitle("ClientMail");
stage.setScene(new Scene(root, 1080, 720));
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
当我尝试 运行 代码时,我遇到了以下问题,我无法理解如何解决它:
Exception in thread "Thread-0" Exception in thread "Thread-1" java.lang.NoClassDefFoundError: Could not initialize class javafx.stage.Screen
at javafx.stage.Window.<init>(Window.java:1439)
at javafx.stage.Stage.<init>(Stage.java:252)
at javafx.stage.Stage.<init>(Stage.java:240)
at model.ParallelClient.run(ParallelClient.java:25)
java.lang.ExceptionInInitializerError
at javafx.stage.Window.<init>(Window.java:1439)
at javafx.stage.Stage.<init>(Stage.java:252)
at javafx.stage.Stage.<init>(Stage.java:240)
at model.ParallelClient.run(ParallelClient.java:25)
Caused by: java.lang.IllegalStateException: This operation is permitted on the event thread only; currentThread = Thread-1
at com.sun.glass.ui.Application.checkEventThread(Application.java:441)
at com.sun.glass.ui.Screen.setEventHandler(Screen.java:369)
at com.sun.javafx.tk.quantum.QuantumToolkit.setScreenConfigurationListener(QuantumToolkit.java:728)
at javafx.stage.Screen.<clinit>(Screen.java:74)
... 4 more
您在问题中发布的应用程序结构存在几个问题。
Application
class 表示整个应用程序的生命周期,通过调用其 init()
、start()
和 stop()
方法来管理。整个应用程序中应该只有一个Application
实例,通常这个实例是由JavaFX启动机制创建的,所以你不应该自己实例化Application
子class。
JavaFX 应用程序需要 JavaFX 运行时间才能启动,其中包括启动 JavaFX 应用程序线程。这是通过调用静态 Application.launch()
方法来完成的,该方法只能调用一次。 launch()
方法启动 JavaFX 运行 时间,创建 Application
class 的实例,调用 init()
,然后在 start()
上调用外汇应用线程。 (在 JavaFX 9 及更高版本中,您还可以通过调用 Platform.startup()
来启动 运行 时间,但这种情况很少见。
请注意,在您的应用程序中,没有调用 Application.launch()
(或 Platform.startup()
),因此 JavaFX 运行time 从未启动。
某些操作只能在 FX 应用程序线程上执行。其中包括创建 Stage
s 和 Scene
s,以及对已显示的 UI 元素的属性进行任何修改。因此,您 不能 在单独的线程中“运行 每个客户端”。这是您的异常的原因:您正在尝试在不是 FX 应用程序线程的线程上创建一个新的 Stage
。
每个客户端不需要一个新线程来显示 UI(而且,如上所述,不能那样做)。您可能确实需要在单独的线程上执行每个客户端与服务器的通信(因为这些操作需要很长时间,并且您不应该阻塞 FX 应用程序线程)。为此,您可以为每个客户端的服务器通信创建一个新线程,或者使用共享执行程序服务,以便每个客户端都可以从池中获取一个线程(这可能是首选方法)。
所以你的结构应该是这样的:
public class Client {
private Parent ui ;
private ExecutorService exec ; // for handling server communication
private final String user ;
public Client(String user, ExecutorService exec) {
this.user = user ;
this.exec = exec ;
try {
ui = FXMLLoader.load(getClass().getResource("ui/client-management.fxml"));
} catch (IOException e) {
e.printStackTrace();
}
}
public Client(String user) {
this(user, Executors.newSingleThreadExecutor());
}
public Parent getUI() {
return ui ;
}
public void showInNewWindow() {
Scene scene = new Scene(ui);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
}
public void checkForNewEmail() {
Task<List<Email>> newEmailTask = new Task<>() {
@Override
protected List<Email> call() throws Exception {
List<Email> newEmails = new ArrayList<>();
// contact server and retrieve any new emails
return newEmails ;
}
};
newEmailTask.setOnSucceeded(e -> {
List<Email> newEmails = newEmailTask.getValue();
// update UI with new emails...
});
exec.submit(newEmailTask);
}
// etc ...
}
然后你的 ClientController
class 可以做这样的事情:
public class ClientController {
private ExecutorService exec = Executors.newCachedThreadPool();
public void startClients() {
Client clientA = new Client("aaaa@gmail.com", exec);
Client clientB = new Client("bbbb@gmail.com", exec);
clientA.showInNewWindow();
clientB.showInNewWindow();
}
}
您的应用程序 class 可以做到
public class ClientApp extends Application {
@Override
public void start(Stage primaryStage) {
ClientController controller = new ClientController();
controller.startClients();
}
public static void main(String[] args) {
Application.launch(args);
}
}
我正尝试在 java 中编写一个非常简单的客户端-服务器电子邮件项目。 我已经使用套接字编写了客户端和服务器之间的通信代码,现在我正在尝试编写一些测试代码,其中还包括一个非常简单的 UI。 我的想法是创建与我拥有的客户一样多的线程,我希望每个线程都开始打开一个简单的 UI window 创建的 Java FX 但我有一些问题。
这是主要的class:
import java.io.*;
public class ClientController{
public static void main(String args[]) throws IOException {
ParallelClient c1=new ParallelClient("aaaa@gmail.com");
ParallelClient c2=new ParallelClient("bbbb@gmail.com");
c1.start();
c2.start();
}
}
这是 ParallelClient class:
import ...
public class ParallelClient extends Thread{
private String user;
public ParallelClient(String user){
this.user=user;
}
public void run(){
ClientApp app=new ClientApp();
try {
app.start(new Stage());
} catch (Exception e) {
e.printStackTrace();
}
...
}
...
}
这是设置新 window:
的 ClientApp classimport ...
public class ClientApp extends Application {
@Override
public void start(Stage stage) throws Exception {
try {
Parent root = FXMLLoader.load(getClass().getResource("ui/client-management.fxml"));
stage.setTitle("ClientMail");
stage.setScene(new Scene(root, 1080, 720));
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
当我尝试 运行 代码时,我遇到了以下问题,我无法理解如何解决它:
Exception in thread "Thread-0" Exception in thread "Thread-1" java.lang.NoClassDefFoundError: Could not initialize class javafx.stage.Screen
at javafx.stage.Window.<init>(Window.java:1439)
at javafx.stage.Stage.<init>(Stage.java:252)
at javafx.stage.Stage.<init>(Stage.java:240)
at model.ParallelClient.run(ParallelClient.java:25)
java.lang.ExceptionInInitializerError
at javafx.stage.Window.<init>(Window.java:1439)
at javafx.stage.Stage.<init>(Stage.java:252)
at javafx.stage.Stage.<init>(Stage.java:240)
at model.ParallelClient.run(ParallelClient.java:25)
Caused by: java.lang.IllegalStateException: This operation is permitted on the event thread only; currentThread = Thread-1
at com.sun.glass.ui.Application.checkEventThread(Application.java:441)
at com.sun.glass.ui.Screen.setEventHandler(Screen.java:369)
at com.sun.javafx.tk.quantum.QuantumToolkit.setScreenConfigurationListener(QuantumToolkit.java:728)
at javafx.stage.Screen.<clinit>(Screen.java:74)
... 4 more
您在问题中发布的应用程序结构存在几个问题。
Application
class 表示整个应用程序的生命周期,通过调用其 init()
、start()
和 stop()
方法来管理。整个应用程序中应该只有一个Application
实例,通常这个实例是由JavaFX启动机制创建的,所以你不应该自己实例化Application
子class。
JavaFX 应用程序需要 JavaFX 运行时间才能启动,其中包括启动 JavaFX 应用程序线程。这是通过调用静态 Application.launch()
方法来完成的,该方法只能调用一次。 launch()
方法启动 JavaFX 运行 时间,创建 Application
class 的实例,调用 init()
,然后在 start()
上调用外汇应用线程。 (在 JavaFX 9 及更高版本中,您还可以通过调用 Platform.startup()
来启动 运行 时间,但这种情况很少见。
请注意,在您的应用程序中,没有调用 Application.launch()
(或 Platform.startup()
),因此 JavaFX 运行time 从未启动。
某些操作只能在 FX 应用程序线程上执行。其中包括创建 Stage
s 和 Scene
s,以及对已显示的 UI 元素的属性进行任何修改。因此,您 不能 在单独的线程中“运行 每个客户端”。这是您的异常的原因:您正在尝试在不是 FX 应用程序线程的线程上创建一个新的 Stage
。
每个客户端不需要一个新线程来显示 UI(而且,如上所述,不能那样做)。您可能确实需要在单独的线程上执行每个客户端与服务器的通信(因为这些操作需要很长时间,并且您不应该阻塞 FX 应用程序线程)。为此,您可以为每个客户端的服务器通信创建一个新线程,或者使用共享执行程序服务,以便每个客户端都可以从池中获取一个线程(这可能是首选方法)。
所以你的结构应该是这样的:
public class Client {
private Parent ui ;
private ExecutorService exec ; // for handling server communication
private final String user ;
public Client(String user, ExecutorService exec) {
this.user = user ;
this.exec = exec ;
try {
ui = FXMLLoader.load(getClass().getResource("ui/client-management.fxml"));
} catch (IOException e) {
e.printStackTrace();
}
}
public Client(String user) {
this(user, Executors.newSingleThreadExecutor());
}
public Parent getUI() {
return ui ;
}
public void showInNewWindow() {
Scene scene = new Scene(ui);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
}
public void checkForNewEmail() {
Task<List<Email>> newEmailTask = new Task<>() {
@Override
protected List<Email> call() throws Exception {
List<Email> newEmails = new ArrayList<>();
// contact server and retrieve any new emails
return newEmails ;
}
};
newEmailTask.setOnSucceeded(e -> {
List<Email> newEmails = newEmailTask.getValue();
// update UI with new emails...
});
exec.submit(newEmailTask);
}
// etc ...
}
然后你的 ClientController
class 可以做这样的事情:
public class ClientController {
private ExecutorService exec = Executors.newCachedThreadPool();
public void startClients() {
Client clientA = new Client("aaaa@gmail.com", exec);
Client clientB = new Client("bbbb@gmail.com", exec);
clientA.showInNewWindow();
clientB.showInNewWindow();
}
}
您的应用程序 class 可以做到
public class ClientApp extends Application {
@Override
public void start(Stage primaryStage) {
ClientController controller = new ClientController();
controller.startClients();
}
public static void main(String[] args) {
Application.launch(args);
}
}