JavaFX ObservableList - 添加项目导致 ConcurrentModificationException

标签 java multithreading javafx thread-safety

我有一张 Albums 的表格可由用户过滤和排序。

这是这张 table 的样子。如您所见,列是可排序的,顶部有一个文本框,当前正在过滤其中包含字符串“cu”的专辑:

User Interface

一切正常专辑列表填充后 .

但是,如果我在填充专辑列表时尝试排序或过滤,我会得到 ConcurrentModificationException .专辑列表最初需要几分钟来加载大型库,我正在使用 WatchService 自动更新列表以更改文件系统,因此在加载列表时禁用功能不是一个选项。

以下是一些简化的代码片段,它们说明了如何设置:

设置列表 (使用提出的想法 here )

public class Library {
    final static ObservableList <Album> albums = FXCollections.observableArrayList( new ArrayList <Album>() );
    final static FilteredList <Album> albumsFiltered = new FilteredList <Album>( albums, p -> true );
    final static SortedList <Album> albumsSorted = new SortedList <Album>( albumsFiltered );
}

建表
public void setupAlbumTable () {
    TableColumn artistColumn = new TableColumn( "Artist" );
    TableColumn yearColumn = new TableColumn( "Year" );
    TableColumn albumColumn = new TableColumn( "Album" );

    artistColumn.setCellValueFactory( new PropertyValueFactory <Album, String>( "albumArtist" ) );
    yearColumn.setCellValueFactory( new PropertyValueFactory <Album, Integer>( "year" ) );
    albumColumn.setCellValueFactory( new PropertyValueFactory <Album, String>( "title" ) );

    albumTable = new TableView();
    albumTable.getColumns().addAll( artistColumn, yearColumn, albumColumn );
    albumTable.setItems( Library.albumsSorted );

    Library.albumsSorted.comparatorProperty().bind( albumTable.comparatorProperty() );

    albumTable.getSortOrder().add( artistColumn );
    albumTable.getSortOrder().add( yearColumn );
    albumTable.getSortOrder().add( albumColumn );
}

设置 FilterBox 和 Predicate
public void setupAlbumFilterPane () {
    TextField filterBox = new TextField();
    filterBox.textProperty().addListener( ( observable, oldValue, newValue ) -> {
        Library.albumsFiltered.setPredicate( album -> {
            if ( newValue == null || newValue.isEmpty() ) {
                return true;
            }

            String[] filterTokens = newValue.toLowerCase().split( "\\s+" );

            ArrayList <String> albumMatchText = new ArrayList <String>();

            albumMatchText.add( album.getAlbumArtist().toLowerCase() );
            albumMatchText.add( album.getTitle().toLowerCase() );
            albumMatchText.add( album.getYear().toLowerCase() );

            for ( String filterToken : filterTokens ) {
                boolean fitlerTokenHasMatch = false;
                for ( String albumInfoString : albumMatchText ) {
                    if ( albumInfoString.contains( filterToken ) ) {
                        fitlerTokenHasMatch = true;
                    }
                }

                if ( !fitlerTokenHasMatch ) {
                    return false;
                }
            }

            return true;
        });
    });
}

从列表中添加数据或删除数据:保证只能从一个线程(不同于 JavaFX)调用这些函数,以及对 ObservableList albums 的所有修改仅由这一个库线程制作。
public class Library {
    ...
    private static void addAlbum ( Album album ) {
        albums.add ( album );
    }

    private static void removeAlbum ( Album album ) {
        albums.remove ( album );
    }
    ...
}

最后,我尝试了两种不同的方法来解决这个问题:

1. 使用 Vector 作为 Observable List 的底层数据结构- 似乎根本没有任何影响。同样的错误。

2. 包装 ObservableList albums 的所有修改在 Platform.callLater .这导致表停止更新:
public class Library {
    ...
    private static void addAlbum ( Album album ) {
        Platform.runLater( new Runnable() {
            public void run() {
                albums.add ( album );
            }
        });
    }

    private static void removeAlbum ( Album album ) {
        Platform.runLater( new Runnable() {
            public void run() {
                albums.remove ( album );
            }
        });
    }
    ...
}

有关如何解决此问题的任何建议?

以下是解决方案的主要特点:
  • 表格 View 在任何时候都是可排序的
  • 表格 View 在任何时候都是可过滤的
  • tableview 会随着专辑列表的变化而定期更新(它不必是即时的,但必须至少每隔几秒更新一次)。
  • 最佳答案

    听起来您是在一个接一个地添加项目。一次添加所有项目不是一种选择?

    关于JavaFX ObservableList - 添加项目导致 ConcurrentModificationException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44099841/

    相关文章:

    java - Spring data 具有多个条件和一个参数的派生查询

    java - 如何删除Ebean ORM实体属性

    c# 如何重构我的线程程序

    java - 如何只打印文本?

    java - 使用 DatePicker 选择多个日期

    java - 匹配java中的任何utf8非空白字符?

    java - 如何强制HashMap使用identity(哈希码)?或建议解决方法

    android - 处理程序无法向主线程传递消息或 Runnable

    python - 多线程脚本的事后调试

    JavaFX - 未调用 runLater Runnable