布尔值不会从 Object.getBoolean() 更新;

Boolean wont Update from Object.getBoolean();

差不多,我正在尝试编写一个让用户选择文件的简单程序。不幸的是,通过 Swing 的 JFileChooser 有点过时了,所以我正在尝试为此使用 JavaFX FileChooser。目标是 运行 FileGetter 作为一个线程,将文件数据传输到 Main Class,然后从那里继续。

主要Class:

package application;
import java.io.File;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;


public class Main {

    public static void main(String[] args) {
        Thread t1 = new Thread(new FileGetter());
        FileGetter fg = new FileGetter();
        t1.start();
        boolean isReady = false;
        while(isReady == false){
            isReady = FileGetter.getIsReady();      
        }
        File file = FileGetter.getFile();

        System.out.println(file.getAbsolutePath());
        ...

    }
}

FileGetter Class:

package application;

import java.io.File;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;


public class FileGetter extends Application implements Runnable {

    static File file;
    static boolean isReady = false;


    @Override
    public void start(Stage primaryStage) {
        try {

            FileChooser fc = new FileChooser();
            while(file == null){
            file = fc.showOpenDialog(primaryStage);
            }
            isReady = true;
            Platform.exit();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
    @Override
    public void run() {
       launch();
    }

    public static boolean getIsReady(){
        return isReady;
    }

    public static File getFile(){
        return file;
    }

}

问题是当用户选择一个文件时,while 循环中 isReady 的值没有更新为 true(我有它的原因是为了防止 Main 中的代码继续将 File 设置为 null) .

非常感谢任何帮助、替代建议或解释为什么会发生这种情况!

在此代码中

 while(isReady == false){
        isReady = FileGetter.getIsReady();      
 }

没有任何东西可以将 FileGetter 的 isReady 状态更改为 true

java 内存模型不要求变量值在不同线程中相同,除非在特定条件下。

这里发生的事情是 FileGetter 线程正在更新自己的内存中的值,该内存只能从该线程访问,但是您的主线程看不到更新后的值,因为它只看到存储在它自己的内存中的变量版本与 FileGetter 线程之一不同。每个线程在内存中都有自己的字段副本,根据 java 规范,这完全没问题。

要解决此问题,您只需将 volatile 修饰符添加到 isReady:

static volatile boolean isReady = false;

这确保更新后的值在您的主线程中可见。

此外,我建议减少您创建的 FileGetter 个实例的数量。在您的代码中创建了 3 个实例,但只使用了 1 个。

Thread t1 = new Thread(() -> Application.launch(FileGetter.class));
t1.start();
...

最简单的实现方式

为什么不遵循标准的 JavaFX 生命周期,而不是试图用手推车来驱动马?换句话说,让你的 Main class 成为 Application 的子 class,在 start() 方法中获取文件,然后继续(在后台线程中) 与应用程序的其余部分一起使用?

public class Main extends Application {

    @Override
    public void init() {
        // make sure we don't exit when file chooser is closed...
        Platform.setImplicitExit(false);
    }

    @Override
    public void start(Stage primaryStage) {
        File file = null ;
        FileChooser fc = new FileChooser();
        while(file == null){
            file = fc.showOpenDialog(primaryStage);
        }
        final File theFile = file ;
        new Thread(() -> runApplication(theFile)).start();
    }

    private void runApplication(File file) {
        // run your application here...
    }

}

你的代码有什么问题

如果您真的希望 Main class 与 JavaFX Application class 分开(这实际上没有意义:一旦您决定使用 JavaFX FileChooser,你已经决定你正在编写一个 JavaFX 应用程序,所以启动 class 应该是 Application 的子 class),然后它变得有点棘手.您的代码目前存在几个问题,其中一些问题已在其他答案中解决。如 Fabian 的回答所示,主要问题是您在不确保活动性的情况下从多个线程引用 FileGetter.isReady。这正是 Josh Bloch 的 Effective Java(第 2 版第 66 项)中解决的问题。

您的代码的另一个问题是您将无法多次使用 FileGetter(您不能多次调用 launch()),这可能不是现在在您的代码中出现问题,但几乎可以肯定,随着开发的进行,此应用程序会在某个时刻出现。问题是您混合了两个问题:启动 FX 工具包和从 FileChooser 检索文件。第一件事只能做一次;第二个应该写成可重用的。

最后是你的循环

while(isReady == false){
    isReady = FileGetter.getIsReady();      
}

是非常糟糕的做法:它尽可能快地检查 isReady 标志。在某些(相当不寻常的)情况下,它甚至可以阻止 FX 应用程序线程拥有 运行 的任何资源。这应该只是阻塞,直到文件准备就绪。


如何在不制作 Main JavaFX 的情况下进行修复 Application

因此,只有当您确实有迫切需要时,我才会首先创建一个 class,它只负责启动 FX 工具包。类似于:

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.stage.Stage;

public class FXStarter extends Application {

    private static final AtomicBoolean startRequested = new AtomicBoolean(false);
    private static final CountDownLatch latch = new CountDownLatch(1);

    @Override
    public void init() {
        Platform.setImplicitExit(false);
    }

    @Override
    public void start(Stage primaryStage) {
        latch.countDown();
    }

    /** Starts the FX toolkit, if not already started via this method,
     ** and blocks execution until it is running.
     **/
    public static void startFXIfNeeded() throws InterruptedException {
        if (! startRequested.getAndSet(true)) {
            new Thread(Application::launch).start();
        }
        latch.await();
    }
}

现在创建一个 class 来为您获取文件。这应该确保 FX 工具包是 运行ning,使用以前的 class。此实现允许您从任何线程调用 getFile()

import java.io.File;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

import javafx.application.Platform;
import javafx.stage.FileChooser;


public class FileGetter {

    /**
     ** Retrieves a file from a JavaFX File chooser. This method can 
     ** be called from any thread, and will block until the user chooses
     ** a file.
     **/

    public File getFile() throws InterruptedException {

        FXStarter.startFXIfNeeded() ;

        if (Platform.isFxApplicationThread()) {
            return doGetFile();
        } else {
            FutureTask<File> task = new FutureTask<File>(this::doGetFile);
            Platform.runLater(task);
            try {
                return task.get();
            } catch (ExecutionException exc) {
                throw new RuntimeException(exc);
            }
        }
    }

    private File doGetFile() {
        File file = null ;
        FileChooser chooser = new FileChooser() ;

        while (file == null) {
            file = chooser.showOpenDialog(null) ;
        }

        return file ;
    }
}

最后你的 Main 只是

import java.io.File;

public class Main {

    public static void main(String[] args) throws InterruptedException {
        File file = new FileGetter().getFile();
        // proceed...
    }
}

同样,这很复杂;我认为没有理由不为此简单地使用标准的 FX 应用程序生命周期,就像答案中的第一个代码块一样。