Java FX废弃线程方法引发GUI冻结问题解决方案咨询
First off, you’re right to suspect those deprecated suspend() and resume() methods—they’re the exact root cause of your random freezes. These methods were marked deprecated for a critical reason: they’re incredibly unsafe. Suspending a thread can leave it holding locks or in an inconsistent state, leading to deadlocks that freeze your app entirely. Let’s replace them with safe, modern alternatives that work seamlessly with JavaFX.
The Best Fix: Use JavaFX’s AnimationTimer
JavaFX has built-in tools for UI animations that eliminate the need for manual thread management. AnimationTimer is perfect here—it runs directly on the FX Application Thread (so no messy Platform.runLater() calls for UI updates) and gives you precise, reliable control over starting/stopping animations.
Here’s a revised version of your controller using AnimationTimer:
package sample; import javafx.animation.AnimationTimer; import javafx.fxml.FXML; import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.effect.DropShadow; import javafx.scene.layout.AnchorPane; import javafx.scene.paint.Color; import javafx.scene.shape.Circle; import javafx.scene.media.Media; import javafx.scene.media.MediaPlayer; import java.io.File; public class Controller { @FXML private Button button; @FXML private AnchorPane pranko; @FXML private Label score; private AnimationTimer rotationTimer; private boolean isRunning = false; private int rotationsRemaining = 0; private double currentRotation = 0; // Track current rotation angle // Sound setup String musicFile = "snap.mp3"; Media sound = new Media(new File(musicFile).toURI().toString()); MediaPlayer mediaPlayer = new MediaPlayer(sound); @FXML private void initialize() { // Start/Stop button initialize Circle circle = new Circle(25); button.setShape(circle); button.setMinSize(50,50); // LCD screen setup score.setStyle("-fx-text-fill: green;" + "-fx-background-color: black"); score.setText("START"); // Add shadow around the button DropShadow dropShadow = new DropShadow(); dropShadow.setRadius(5); dropShadow.setColor(Color.GREEN); button.setEffect(dropShadow); // Initialize rotation timer logic rotationTimer = new AnimationTimer() { @Override public void handle(long now) { if (!isRunning || rotationsRemaining <= 0) { stopAnimation(); return; } // Update rotation (matches your original 5ms per step timing) currentRotation += 1; if (currentRotation >= 360) { currentRotation = 0; rotationsRemaining--; score.setText(String.valueOf(rotationsRemaining)); } pranko.setRotate(currentRotation); } }; } @FXML private void toggle() { if (isRunning) { // Pause the animation isRunning = false; updateButtonState("start", Color.GREEN, "#0cf800"); score.setStyle("-fx-text-fill: red;" + "-fx-background-color: black"); score.setText("PAUSED"); } else { // Start/resume the animation isRunning = true; if (rotationsRemaining == 0) { rotationsRemaining = 10; // Reset rotation count if starting fresh currentRotation = 0; score.setText(String.valueOf(rotationsRemaining)); } updateButtonState("stop", Color.RED, "red"); score.setStyle("-fx-text-fill: green;" + "-fx-background-color: black"); rotationTimer.start(); } } private void stopAnimation() { isRunning = false; rotationTimer.stop(); updateButtonState("start", Color.GREEN, "#0cf800"); score.setStyle("-fx-text-fill: red;" + "-fx-background-color: black"); score.setText("END"); } private void updateButtonState(String text, Color shadowColor, String textColor) { button.setText(text); button.setStyle("-fx-text-fill: " + textColor); DropShadow dropShadow = new DropShadow(); dropShadow.setRadius(5); dropShadow.setColor(shadowColor); button.setEffect(dropShadow); } }
Key Improvements Over Your Original Code:
- No risky thread management:
AnimationTimerhandles the animation loop safely on the FX thread, eliminating all the dangers of raw threads and deprecated methods. - Smooth, predictable control: The
isRunningboolean lets you start/stop the animation cleanly without leaving threads in inconsistent states. - Less boilerplate: No need for
Platform.runLater()because all UI updates happen on the correct thread automatically. - Clean state tracking: We explicitly track
currentRotationandrotationsRemaining, so the animation picks up exactly where it left off when resumed.
Why This Fixes Your Freeze Issue
The deprecated suspend() method could halt the thread at any point—even mid-UI update or while holding a resource lock. This led to unpredictable deadlocks where your app couldn’t resume the thread. AnimationTimer uses JavaFX’s built-in concurrency model, which is designed to avoid these kinds of issues entirely.
If you really want to stick with a manual thread approach (though AnimationTimer is strongly recommended), you could use an AtomicBoolean to control the thread’s loop and wait()/notify() for pausing, but that adds unnecessary complexity for a UI animation.
Give this revised code a try—it should eliminate the random freezes and make your washer app run reliably!
内容的提问来源于stack exchange,提问作者FilipA

