Jak zatrzymać wątek w javaFX podczas zamykania aplikacji krzyżykiem.

0

Mam klasę implementującą interfejs runnable:

public class ThreadInterfaceImplementation implements Runnable {

private volatile boolean running = true;

 public void terminate() {
        running = false;
    }

 @Override
    public void run() {
            while(running)
             {
                   System.out.println("I'm still running");
             }
   }
}

Moja klasa główna wygląda tak:


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

import java.io.IOException;


public class App extends Application {

    private static Scene scene;

    @Override
    public void start(Stage stage) throws IOException {
        scene = new Scene(loadFXML("layout").load(), 630, 400);
        stage.setScene(scene);
        stage.show();
    }

    static void setRoot(String fxml) throws IOException {
        scene.setRoot(loadFXML(fxml).load());
    }

    private static FXMLLoader loadFXML(String fxml) throws IOException {
        FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource(fxml + ".fxml"));
        return fxmlLoader;
    }


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

}

W klasie którą wywołuje klasa App po przez scene = new Scene(loadFXML("layout").load(), 630, 400) mam powiedzmy, że coś takiego:

public class Controller implements Initializable {

    private ThreadInterfaceImplementation runnable = null;
    private Thread thread = null;
  
    @Override
    public void initialize (URL url, ResourceBundle resourceBundle) {
        runnable = new ThreadInterfaceImplementation();
        thread = new Thread(runnable);
        thread.start();
    }
}

I teraz chcę zamknąć wątek przy wyłączaniu aplikacji krzyżykiem. Jak mam to zrobić?

0

Albo https://docs.oracle.com/javase/tutorial/essential/concurrency/interrupt.html, albo robisz własna podklasę Thread, która umie ustawić ten running na false

1

Zależy co robisz w pętli while(true) jeśli tak jak w przykładzie to wystarczy wywołać metodę terminate podczas kliknięcia krzyżyka zamykającego. Czyli na ten moment musiałbyś tam przekazać referencję do ThreadInterfaceImplementation żeby móc skorzystać z tej metody.

Sprawa zaczyna się komplikować jeśli program nie jest wait-free, np ciało metody run zawiera blokujący odczyt z socketa lub próbuje coś zdjąć / wrzucić na blokującą przepełnioną kolejkę - w takim przypadku może w ogóle nie dojść do kolejnego sprawdzenia warunki w pętli while. Wtedy musimy zaprogramować cancellation za pomocą interruption. W poor designed API mamy blokujące operację typu wait sleep join, one z definicji respektują interupcję rzucając wyjątek InterruptedException, więc można zdefiniować tzw cancellation points i na tym się opierać.

Dobrą praktyką jest propagacją tego statusu do czegoś wyżej, co wie jak odpowiednio na to zareagować, czyli re-throw wyjątku lub jeśli nie chcemy / nie da się (jak w przypadku Runnable) to ustawiamy status z powrotem za pomocą metody Thread.interrupt()

Sprawa jeszcze bardziej się komplikuje jakbyś chciał zrobić coś sensownego przed zamknięciem aplikacji (dokończyć transakcję?, zwolnić zasoby?) albo jeśli korzystasz z puli wątków - nie chcesz zabijać "nie swoich" wątków - stąd dla większości przypadków najwygodniej jest po prostu użyć klas Executor. To API pozwala zrobić w prosty sposób graceful,shutdown, odseparować warstwy oraz zapewnia tzw publikację (tzn widoczność dokonanych zmian dla innych wątków) co też przy większej aplikacji nie jest takie proste, mutowanie zewsząd zmiennych volatile w zazwyczaj nie udokumentowanej strukturze to pewne błędy

0

@pedegie Dzięki z tak obszerną i... skomplikowaną odpowiedź. :)
Ale może na początek podstawy?
W jaki sposób mam przechwycić to kliknięcie krzyżyka? Wiem, że można to zrobić, np. implementując metodę stop() w klasie App. Ale w jaki sposób mam w klasie App przekazać referencje do obiektu znajdującego się w klasie Controller?

Bo szczerze mówiąc Java to pÓÓÓÓki co jest dla mnie czarna magia. Wcześniej pisałem tylko w c++, Jave dopiero zaczynam ogarniać. I w tym kodzie na klasę App (który podali nam na studiach) dzieją się dla mnie cuda.

Z konstruktora:
Scene(Parent root, double width, double height)

funkcji:
public <T> T load()
public final void setScene(Scene value)

i pliku fxml

w jakiś magiczny sposób przechodzimy do obiektu klasy Controller.

Z kolei w klasie Controller możemy wywolać funkcję:
App.setRoot("jakiś_inny_layout")

I znowu magicznie przechodzimy do obiektu innej klasy.

0

Jak chcesz wywołać jakąś funkcję przy zamykaniu aplikacji to w JavaFX masz coś takiego jak: https://www.programcreek.com/java-api-examples/?class=javafx.stage.Stage&method=setOnCloseRequest, co do zatrzymywania wątków, najlepiej jakbyś korzystał z ExecutorService https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html i na nim wykonał shutdown() w momencie zamykania aplikacji.

1 użytkowników online, w tym zalogowanych: 0, gości: 1