JavaFX + Java + Task +Thread. Как организовать многозадачность?

Пишу клиент серверное приложение используя Socket, ServerSocket. В качестве графики использую javaFx. Чтобы отображать сообщения всех пользователей в 'TextArea printMessageField' создал в классе Client отдельный поток (Task + Thread) для получения сообщений от сервера. В чем заключается проблема:

  1. Если не использовать поток, то после нажатия на кнопку "Войти в чат", программа начинает не отвечать и закрывается. Все из-за цикла while (true), что служит для получения сообщений от сервера.
  2. С добавлением потока стало все лучше, но не полностью. Теперь после нажатия на кнопку "Войти в чат" программа не ломается. Далее, если выйти из чата (нажать кнопку "Выйти из чата") и попробовать обратно зайти, то программа виснет. (Вас перекинет в окно авторизации, я вырежу код, что отвечает за это. Необходимо нажать только на кнопку "Войти").
  3. С добавлением потока также после закрытия окна (нажать на крестик справа сверху окна) IntelliJ показывает, что программа все еще работает. (Убрал все проверки после нажатия на кнопку "Войти", программа перестала виснуть, но после закрытия все равно IntelliJ показывает, что программа работает).

Вопрос: как это исправить?

Main.java

package app;

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

import java.io.IOException;

public class Main extends Application{

    public static void main(String[] args) {

        launch(args);
    }

    @Override
    public void start(Stage stage) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("view/chatWindow.fxml"));
        Scene scene = new Scene(fxmlLoader.load(), 800, 600);
        stage.setScene(scene);
        stage.show();
    }
}

Controller.java

package app.controllers;

//Соединение с сервером
import java.io.*;
import java.net.Socket;

//Графика
import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.stage.Stage;

public class Controller {

    @FXML
    private ResourceBundle resources;

    @FXML
    private URL location;

    @FXML
    private Label errorText;

    @FXML
    private Button authorizationButton;

    @FXML
    private TextField login_field;

    @FXML
    private PasswordField password_field;

    @FXML
    private Button registrationButton;

    @FXML
    void initialize() {
        //Действия для кнопки входа
        authorizationButton.setOnAction(actionEvent -> {
            authorizationButton.getScene().getWindow().hide();
            FXMLLoader fxmlLoader = new FXMLLoader();
            fxmlLoader.setLocation(getClass().getResource("/app/view/chatWindow.fxml"));

            try {
                fxmlLoader.load();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }

            Parent root = fxmlLoader.getRoot();
            Stage stage = new Stage();
            stage.setScene(new Scene(root));
            stage.show();
        });
    }
}

ChatController.java

package app.controllers;

import java.io.*;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.stage.Stage;

public class ChatController {
    private Client client;
    //Показывет есть ли подключение или нет, чтобы другие кнопки не работали
    private static boolean statusConnect;

    @FXML
    private ResourceBundle resources;

    @FXML
    private URL location;

    @FXML
    private Button enterChatButton;

    @FXML
    private Button exitChatButton;

    @FXML
    private TextField inputMessageField;

    @FXML
    private Button menuFuncButton;

    @FXML
    private static TextArea printMessageField;

    @FXML
    private static TextArea printUsersField;

    @FXML
    private Button sendFileButton;

    @FXML
    private Button sendMessageButton;

    @FXML
    void initialize() {
        //Кнопка входа
        enterChatButton.setOnAction(actionEvent -> { //сделать потеряно соединение вдруг что
            client = new Client();
            statusConnect = true;
        });

        //Кнопка выхода из чата
        exitChatButton.setOnAction(actionEvent -> {
            if (statusConnect){
                statusConnect = false;
                //Закрываем поток
                try {
                    client.exitChat();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }

                //Смена окна
                exitChatButton.getScene().getWindow().hide();
                FXMLLoader fxmlLoader = new FXMLLoader();
                fxmlLoader.setLocation(getClass().getResource("/app/view/app.fxml"));

                try {
                    fxmlLoader.load();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }

                Parent root = fxmlLoader.getRoot();
                Stage stage = new Stage();
                stage.setScene(new Scene(root));
                stage.show();
            } else {
                System.out.println(1);
            }
        });
    }
}

Client.java

package app.controllers;

import javafx.concurrent.Task;
import java.io.*;
import java.net.Socket;

public class Client  {
    private boolean status;
    private Socket socket;
    private BufferedReader reader;
    private BufferedWriter writer;

    Client () {
        new Thread(task).start();
    }

    Task<Void> task = new Task<Void>() {
        @Override
        public Void call() throws Exception {
            try {
                socket = new Socket("127.0.0.1", 1821);
                writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                status = true;
                while (status){
                    //тут клиент должен получать сообщения от сервера
                    reader.readLine();
                }

                socket.close();
                writer.close();
                reader.close();

            } catch (IOException e) {
                //Тут добавлял return null не помогло
                e.printStackTrace();
            }
            return null;
        }
    };

    public void exitChat() throws IOException {
        status = false;
    }
}

App.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.PasswordField?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>

<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0" style="-fx-background-color: #ffca77;" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.controllers.Controller">
   <children>
      <Label layoutX="341.0" layoutY="165.0" text="Авторизация">
         <font>
            <Font size="24.0" />
         </font>
      </Label>
      <TextField fx:id="login_field" layoutX="326.0" layoutY="222.0" promptText="Логин" style="-fx-background-color: #ffca77;">
         <font>
            <Font size="14.0" />
         </font>
      </TextField>
      <PasswordField fx:id="password_field" layoutX="326.0" layoutY="270.0" promptText="Пароль" style="-fx-background-color: #ffca77;">
         <font>
            <Font size="14.0" />
         </font>
      </PasswordField>
      <Button fx:id="authorizationButton" layoutX="326.0" layoutY="329.0" mnemonicParsing="false" text="Вход">
         <font>
            <Font size="14.0" />
         </font>
      </Button>
      <Button fx:id="registrationButton" layoutX="400.0" layoutY="329.0" mnemonicParsing="false" text="Регистрация">
         <font>
            <Font size="14.0" />
         </font>
      </Button>
      <Label fx:id="errorText" layoutX="524.0" layoutY="200.0" prefHeight="127.0" prefWidth="222.0" />
   </children>
</AnchorPane>

chatWindow.fxml

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.Font?>

<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0" style="-fx-background-color: #ffca77;" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.controllers.ChatController">
   <children>
      <TextArea fx:id="printMessageField" editable="false" layoutX="14.0" layoutY="43.0" prefHeight="461.0" prefWidth="538.0" />
      <TextField fx:id="inputMessageField" layoutX="14.0" layoutY="528.0" prefHeight="26.0" prefWidth="429.0">
         <font>
            <Font size="14.0" />
         </font>
      </TextField>
      <Button fx:id="sendMessageButton" layoutX="461.0" layoutY="527.0" mnemonicParsing="false" prefHeight="30.0" prefWidth="90.0" text="Отправить">
         <font>
            <Font size="14.0" />
         </font>
      </Button>
      <TextArea fx:id="printUsersField" editable="false" layoutX="584.0" layoutY="149.0" prefHeight="281.0" prefWidth="200.0" />
      <Label layoutX="617.0" layoutY="117.0" text="Пользователи в чате">
         <font>
            <Font size="14.0" />
         </font>
      </Label>
      <Button fx:id="exitChatButton" layoutX="676.0" layoutY="556.0" mnemonicParsing="false" text="Выйти из чата">
         <font>
            <Font size="14.0" />
         </font>
      </Button>
      <Button fx:id="sendFileButton" layoutX="444.0" layoutY="558.0" mnemonicParsing="false" text="Отправить файл">
         <font>
            <Font size="14.0" />
         </font>
      </Button>
      <Button fx:id="menuFuncButton" layoutX="589.0" layoutY="43.0" mnemonicParsing="false" text="Дополнительные функции">
         <font>
            <Font size="14.0" />
         </font>
      </Button>
      <Button fx:id="enterChatButton" layoutX="684.0" layoutY="519.0" mnemonicParsing="false" text="Войти в чат">
         <font>
            <Font size="14.0" />
         </font>
      </Button>
   </children>
</AnchorPane>

Server.java

package server;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {

    public static void main(String[] args){
        try(ServerSocket server = new ServerSocket(1821)){
            System.out.println("Server started");

            while (true) {
                try (
                        Socket socket = server.accept();
                        BufferedWriter writer =
                                new BufferedWriter(
                                        new OutputStreamWriter(
                                                socket.getOutputStream()));

                        BufferedReader reader =
                                new BufferedReader(
                                        new InputStreamReader(
                                                socket.getInputStream()))
                ) {
                   //Тут код вырезан

                } catch (Exception ignored) {
                }
            }

        } catch (IOException e){
            throw new RuntimeException(e);
        }
    }
}

Ответы (0 шт):