首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >单击按钮后,JavaFX得到无限循环错误

单击按钮后,JavaFX得到无限循环错误
EN

Stack Overflow用户
提问于 2017-11-05 19:12:45
回答 1查看 902关注 0票数 0

我发布了一个类似于此的问题,但我不够具体。下面是我的代码的简化版本:

代码语言:javascript
复制
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.scene.control.ProgressBar;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.layout.HBox;
import javafx.scene.Scene;
import javafx.concurrent.Task;
   import javafx.stage.Stage;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.scene.control.Button;
import javafx.scene.control.MenuBar;
import javafx.scene.control.Menu;
 import javafx.scene.control.MenuItem;
import javafx.scene.control.Separator;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.text.Text;
 import javafx.scene.text.Font;
import javafx.geometry.Pos;
import java.net.URL;
import javafx.scene.image.Image;
import javafx.scene.layout.GridPane;
import javafx.scene.control.Slider;

public class ProgressTest extends Application {

boolean play = true;
int x = 0;
@Override
public void start(Stage stage) {
GridPane pane = new GridPane(); //pane and Hboxes
HBox hbox = new HBox();

Button update = new Button("Start");
update.setOnAction( e -> {
    while(play == true)
    {
        System.out.println(++x);
    }
});

Button pause = new Button("Pause");
pause.setOnAction( e -> {
    if(play == true)
    {
        pause.setText("Play");
        play = false;
    }

    else
    {
        pause.setText("Pause");
        play = true;
    }
});


hbox.getChildren().addAll(update, pause);
pane.add(hbox, 0, 1);
Scene scene = new Scene(pane);
stage.setMaxWidth(655);
stage.setMaxHeight(620);

stage.setTitle("Gallery!");
stage.setScene(scene);

stage.sizeToScene();

stage.show();

}


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

}

这段代码的思想是,当用户单击“开始”按钮时,程序应该打印出向上的x,并在用户点击“暂停”时暂停,当用户点击"Play“时再继续。

问题是,每当我单击"Play“按钮时,程序就会进入一个无限循环,并且我无法按下暂停按钮来停止它。我这样做有什么不对吗?有什么窍门能让这件事成功吗?任何帮助都将不胜感激。

此外,我知道其中一些可能存在语法错误,但我知道代码副本是正确的,我更多地考虑了如何使其工作的逻辑。

EN

回答 1

Stack Overflow用户

发布于 2017-11-05 23:20:42

您的实现有两个大问题:

  1. GUI运行在JavaFX线程上。为了保持响应性,它中的任何操作都必须迅速完成。当您在这个线程上运行像您的循环那样长的计算时,整个GUI就会被阻塞。
  2. 在您设法将play设置为false之后,循环将退出,单击简历将什么也做不了。

有几种方法可以解决这个问题。我将使用ThreadCountDownLatch演示一个。

我无法解释问题范围内的线程是什么(到处都有大量的材料),但这里的相关问题是,在一个不是JavaFX线程的线程上执行代价高昂的操作将解决第一点。

CountDownLatch用于阻止线程(或多个)的执行,直到锁存/断开为止,线程将继续执行锁存。它是用一个int初始化的,它表示在发布之前需要计数的次数。到达闩锁的await方法的线程会阻塞,直到闩锁的countDown方法被调用指定的次数为止。

下面是一个示例代码:

代码语言:javascript
复制
import java.util.concurrent.CountDownLatch;

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class ProgressTest extends Application {

    volatile CountDownLatch cl = new CountDownLatch(1);

    @Override
    public void start(Stage stage) {
        Thread thread = new Thread(() -> {
            int x = 0;
            while (true) {
                try {
                    cl.await();
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                }
                System.out.println(++x);
            }
        });
        thread.setDaemon(true);

        Button update = new Button("Start");
        update.setOnAction(e -> {
            if (!thread.isAlive()) {
                cl.countDown();
                thread.start();
            }
        });

        BooleanProperty running = new SimpleBooleanProperty(true);

        Button pause = new Button("Pause");
        pause.textProperty().bind(Bindings.when(running).then("Pause").otherwise("Play"));
        pause.setOnAction(e -> {
            if (running.get()) {
                cl = new CountDownLatch(1);
            }
            else {
                cl.countDown();
            }
            running.set(!running.get());
        });

        HBox hbox = new HBox(update, pause);
        Scene scene = new Scene(hbox);
        stage.setScene(scene);
        stage.setTitle("Gallery!");
        stage.sizeToScene();
        stage.show();
    }

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

线程与while(true)循环一起“尽可能快地”释放数字,只有通过到达await方法才能暂停它。当您按Start时,闩锁断了,线程继续启动和执行。当您按下“暂停”时,就会在它的位置创建一个新的锁存器(闩锁是一个一次性的东西,不能重置),这会导致线程在await中等待,直到有人用countDown方法破坏它(一次调用就足够了,因为我们用1实例化了它)。这就是按压简历所做的。

在线程上调用setDaemon(true)确保它允许JVM退出而无需等待它。如果线程必须在JVM存在之前完成(例如,它不是后台线程),则可以删除它。

Iv'e制作了锁存器volatile,它保证不同的线程会看到相同的值。还请参阅您是否在Java中使用过易失性关键字?和其他可用资源。在这种特殊情况下,您不需要点点线程同步,因此它不会产生明显的效果,但它仍然应该存在。

请注意,我在Start上添加了一个小的检查,即线程尚未运行,因为在线程运行时启动线程会引发异常。如果在执行过程中按下Start,您没有指定该做什么。

虽然与您的问题无关,但Iv'e演示了如何利用JavaFX结合 API使按钮的文本更新自动具有boolean的值。我将控件boolean“提升”到属性,并将按钮的文本绑定到其值。不过,在这种情况下,它可能不太有用。

备注:

  • 您正在设置场景的高度和宽度,然后调用sizeToScene,这使得前面的调用变得多余。
  • 您不需要使用==检查布尔值,您可以直接使用它:if (b == true)等效于if (b)if (b == false)等效于if (!b)
  • 您的boolean更好的名称是表示状态(“运行”/“暂停”),而不是动作(“运行”/“暂停”)。
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/47125540

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档