Метод проверяет блокировку, запускается после снятия блокировки - PullRequest
0 голосов
/ 06 июня 2018

Я столкнулся с потенциальной ошибкой погони.Я использую javafx TableView для отображения моих данных, и я периодически получаю запрос на обновление извне, который вызывает мою функцию обновления.У меня также есть некоторые слушатели, которые выполняют такие вещи, как обработка событий mousedrag и т. Д. Я хочу сделать что-то вроде этого:

private void handleEvent(){
  TableView.setRowFactory(new Callback<TableView<MyModel>, TableRow<MyModel>>(){
    public TableRow<MyModel> call(TableView<MyModel> p) {
      final TableRow row = new TableRow();
      row.setOnDragDetected(new EventHandler<MouseEvent>(){
        public void handle(){
          //implement some kind of lock to prevent receiving data update
        }
      }
      row.setOnMouseDragExited(new EventHandler<MouseDragEvent>(){
        //release lock to accept update
      }
   }
}

//this method is being called externally periodically
public void updateModel(MyModel model){
  //this won't work because it will skip entirely if it's locked,
  //I want it to instead run later when lock is released
  if (!locked){
    this.model = model;
  }
}

Я сделал быстрый обходной путь, используя Boolean для блокировки и разблокировки, какпоказанный в методе updateModel, проблема в том, что он потеряет некоторые обновленные данные, потому что он полностью пропущен ... вместо этого я хочу, чтобы он запускался позже, когда снята блокировка ... как я могу реализовать этот вид механизма блокировки и запустить функцию позже?

Изменить: почему я подозреваю, что это потому, что мои слушатели манипулируют и получают данные таблицы ... в то время как данные постоянно обновляются, я не уверен, что это приводит к разрыву моей таблицы.

1 Ответ

0 голосов
/ 06 июня 2018

Просто напишите некоторую логику, которая собирает все, что вы пытались сделать в заблокированном состоянии, и выполняет его при разблокировке.

В следующем коде предполагается, что вы используете Platform.runLater или аналогичный код, который запускает обновлениепоток приложения.

public class UpdateSynchronizer {

    private final List<Runnable> pendingUpdates = new ArrayList<>();
    private boolean locked = false;

    public void lock() {
        if (locked) {
            throw new IllegalStateException("double lock");
        } else {
            locked = true;
        }
    }

    public void runUpdate(Runnable updater) {
        if (updater == null) {
            throw new IllegalArgumentException();
        }

        if (locked) {
            pendingUpdates.add(updater);
        } else {
            updater.run();
        }
    }

    public void unlock() {
        for (Runnable r : pendingUpdates) {
            try {
                r.run();
            } catch(Exception ex) {
                ex.printStackTrace(); // print but ignore
            }
        }
        pendingUpdates.clear();
        locked = false;
    }

}

Если при последнем обновлении все данные предыдущих обновлений всегда перезаписываются, простое сохранение одного Runnable вместо их списка будет более производительным.

private final UpdateSynchronizer synchronizer = new UpdateSynchronizer();

// why did all the keywords start with uppercase letters (compile time error)
private void handleEvent(){
  TableView.setRowFactory(new Callback<TableView<myModel>, TableRow<myModel>>(){
    public TableRow<myModel> call(TableView<myModel> p) {
      final TableRow row = new TableRow();
      row.setOnDragDetected(new EventHandler<MouseEvent>(){
        public void handle(){
           synchronizer.lock();
           //implement some kind of lock to prevent receiving data update
        }
      }
      row.setOnMouseDragExited(new EventHandler<MouseDragEvent>(){
        //release lock to accept update
        synchronizer.unlock();
      }
   }
}

//this method is being called externally periodically
public void updateModel(myModel model){
  synchronizer.runUpdate(() -> {
      // this is just an assignment and won't have any side effects
      // updates to the scene may only happen, if the model is accessed in some event handler or animation
      this.model = model;

  });
}
...