Как правильно реализовать функции запуска и приостановки в JavaFX? - PullRequest
0 голосов
/ 10 декабря 2018

Я создаю JavaFX, который сканирует ваш почтовый ящик.

Есть 2 кнопки, одна для запуска сканирования и одна для приостановки сканирования.

Чтобы реализовать это, я создал новый поток, передавая его в исполняемый файл с функцией scanInbox ()Позвонил.

Однако, когда я нажимаю кнопку паузы, чтобы вызвать thread.wait (), она застревает.

Каков наилучший способ реализовать эту функцию здесь?

public class WebsiteOverviewController {

    @FXML
    private TableView<Website> deleteTable;

    @FXML
    private TableColumn<Website, String> deleteColumn;

    @FXML
    private TableView<Website> keepTable;

    @FXML 
    private TableColumn<Website, String> keepColumn;

    @FXML
    private JFXButton scanButton;

    @FXML
    private JFXButton pauseButton;

    private BooleanProperty isScanning = new SimpleBooleanProperty(false);

    private MainApp mainApp;

    private FilteredList<Website> keepData;

    private FilteredList<Website> deleteData;

    Task<Void> task;
    Thread thread;


    public WebsiteOverviewController() {

    }

    @FXML
    public void initialize() {
        deleteColumn.setCellValueFactory(cellData -> cellData.getValue().websiteProperty());
        keepColumn.setCellValueFactory(cellData -> cellData.getValue().websiteProperty());

        scanButton.visibleProperty().bind(isScanning.not());
        pauseButton.visibleProperty().bind(isScanning);
    }

    public void setMainApp(MainApp mainApp) {
        this.mainApp = mainApp;

        keepData = new FilteredList<>(mainApp.getWebsiteData(), p -> p.getKeep());
        deleteData = new FilteredList<>(mainApp.getWebsiteData(), p -> !p.getKeep());
        deleteTable.setItems(deleteData);
        keepTable.setItems(keepData);
    }

    @FXML
    public void handleScanInbox() {
        isScanning.set(true);


        thread = new Thread(new Runnable() {

            @Override
            public void run() {
                mainApp.handleScanInbox();
            }

        });
        thread.start();


    }

    @FXML
    public void handlePauseScanInbox() {
        isScanning.set(false);
        try {
            synchronized(thread) {
                thread.wait();
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }


}

Ответы [ 2 ]

0 голосов
/ 11 декабря 2018

Решение Алекса работает нормально, но если вы все еще хотите использовать более низкий уровень управления потоками (wait-notify), вот как вы можете это сделать:

public class WebsiteOverviewController {
    @FXML
    public void handleScanInbox() {
        isScanning.set(true);

        thread = new Thread(mainApp::handleScanInbox);
        thread.start();
    }

    @FXML
    public void handlePauseScanInbox() {
        isScanning.set(false);
        mainApp.pause();
    }

    // Another handler for resuming...
}

public class MainApp {
    private final AtomicBoolean paused = new AtomicBoolean(false);

    public void handleScanInbox() {
        for (int i = 0; i < numberOfItems; i++) { // This could be a while loop
            synchronized (paused) {
                while (paused.get()) { // Using while so that it could re-wait in the case that the object was falsely notified
                    try {
                        pause.wait();
                    }
                    catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }

            // Do whatever you need to do
        }
    }

    public void pause() {
        pause.compareAndSet(false, true);
    }

    public void resume() {
        synchronized (paused) {
            if (paused.get()) {
                paused.set(false);
                paused.notify();
            }
        }
    }
}

Обновление

Если вы хотите иметь возможность вызывать конкретный объект для переключения между приостановкой и возобновлением, вы можете добавить для этого другой метод:

// In MainApp
public void togglePauseResume() {
    synchronized (paused) {
        if (paused.get()) {
            paused.set(false);
            paused.notify();
        }
        else {
            paused.set(true); // You don't need compareAndSet() because paused object has been used for synchronization (i.e. locked)
        }
    }
}

В любом случае вам следует попытаться избежать этого:

@FXML
public void handleButton() {
    if (mainApp.isPaused()) { // You added a getter for paused
        mainApp.pause();
    }
    else {
        mainApp.resume();
    }
}

Это потому, что MainApp.paused может потенциально меняться между геттером и pause() (то есть, состояние гонки).

Обновление 2

Если вы просто хотите использоватьединственный метод для запуска / возобновления потока, вы можете просто создать поток, если он null, или вызвать resume() в противном случае.

@FXML
public void handleScanInbox() {
    isScanning.set(true); // Not sure if you still need this

    if (thread == null) {
        thread = new Thread(mainApp::handleScanInbox);
        thread.start();
    }
    else if (thread.isAlive()) {
        mainApp.resume();
    }
}

Я также изменил if (paused.get()) на while (paused.get())В случае, если notify() был вызван paused случайно.

0 голосов
/ 10 декабря 2018

Вы можете реализовать его с помощью AtomicBoolean, установить его значение true в режиме паузы и проверить его внутри вашего метода mainApp.handleScanInbox ().Вы можете проверить, останавливается ли он на каждой итерации, каждые 10 итераций или при каждом запуске метода, в зависимости от ваших требований

    AtomicBoolean paused = new AtomicBoolean(false);

    @FXML
    public void handlePauseScanInbox() {
        paused.compareAndSet(false,true);
    }

    //mainApp.handleScanInbox();
    public void handleScanInbox(AtomicBoolean paused){
        for(/* your entire inbox*/){
            while(paused.get()){
                TimeUnit.SECONDS.sleep(3);
            }
        }
    }
...