Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading Propper JavaFX线程实现_Multithreading_Javafx_Concurrency_Task - Fatal编程技术网

Multithreading Propper JavaFX线程实现

Multithreading Propper JavaFX线程实现,multithreading,javafx,concurrency,task,Multithreading,Javafx,Concurrency,Task,在我的GUI中,我有一个TableView,在一个名为PathGetter的类将加载的文件加载到ObservableArrayList中之后,它应该显示一个加载文件的列表,但我无法正确地实现该任务 这是JavaFX类中的重要部分 browseButton.setOnAction(event -> { File dir = folderPicker.showDialog(bwindow); if(dir != null){

在我的GUI中,我有一个TableView,在一个名为PathGetter的类将加载的文件加载到ObservableArrayList中之后,它应该显示一个加载文件的列表,但我无法正确地实现该任务

这是JavaFX类中的重要部分

browseButton.setOnAction(event -> {
            File dir = folderPicker.showDialog(bwindow);
            if(dir != null){
                directoryLocation.setText(String.valueOf(dir));
                bottom.getChildren().add(new javafx.scene.control.Label("Loading Tracks"));
                //PathGetter.getPath(directoryLocation.getText());
                PathGetter task = new PathGetter(directoryLocation.getText());
                Thread th = new Thread(task);
                try {
                    pjesme = FXCollections.observableArrayList();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                selection.setItems(pjesme);
                chSelAll.setDisable(false);
                chSelIncomplete.setDisable(false);
                chSelNoCover.setDisable(false);
            }

        });
这是一门应该学习的课程

public class PathGetter extends Task<ObservableList<Track>> {

    static boolean getSubDirs;
    static ArrayList <Track> allFiles;
    public static int trNr = 0;
    private static String fullPath;

    public PathGetter(String path) {
        fullPath = path;
    }

    public static int getTrNr() {
        return trNr;
    }

    public static void setTrNr(int trNr) {
        PathGetter.trNr = trNr;
    }

    public static boolean isSupported (File f){
        //supported file types
        if(String.valueOf(f).endsWith(".flac") || String.valueOf(f).endsWith(".mp3") || String.valueOf(f).endsWith(".aiff") || String.valueOf(f).endsWith(".ogg") || String.valueOf(f).endsWith(".mp4")){
            return true;
        }else{
            return false;
        }
    }

    @Override
    protected ObservableList<Track> call() throws Exception {
        getSubDirs = Browser.chSubDirs.isSelected();
        allFiles = new ArrayList<Track>();
        Queue<File> dirs = new LinkedList<File>();
        dirs.add(new File(fullPath));
        while (!dirs.isEmpty()) {
            for (File f : dirs.poll().listFiles()) {
                if (f.isDirectory() && getSubDirs == true) {
                    dirs.add(f);
                } else if (f.isFile() && isSupported(f)) {
                    allFiles.add(new Track(f));
                    setTrNr(getTrNr()+1);
                }
            }
        }
        ObservableList<Track> returnList = FXCollections.observableArrayList(allFiles);
        return returnList;
    }
}
公共类PathGetter扩展任务{
静态布尔函数;
静态ArrayList所有文件;
公共静态int trNr=0;
私有静态字符串完整路径;
公共路径(字符串路径){
完整路径=路径;
}
公共静态int getTrNr(){
返回trNr;
}
公共静态无效设置trNr(内部trNr){
PathGetter.trNr=trNr;
}
公共静态布尔值isSupported(文件f){
//支持的文件类型
if(String.valueOf(f).endsWith(“.flac”)| String.valueOf(f).endsWith(“.mp3”)| String.valueOf(f).endsWith(“.aiff”)| String.valueOf(f).endsWith(“.ogg”)| String.valueOf(f).endsWith(.mp4”)){
返回true;
}否则{
返回false;
}
}
@凌驾
受保护的ObservableList调用()引发异常{
getSubDirs=Browser.chSubDirs.isSelected();
allFiles=new ArrayList();
Queue dirs=new LinkedList();
添加(新文件(完整路径));
而(!dirs.isEmpty()){
对于(文件f:dirs.poll().listFiles()){
if(f.isDirectory()&&getSubDirs==true){
直接添加(f);
}else if(f.isFile()&&isSupported(f)){
添加(新曲目(f));
setTrNr(getTrNr()+1);
}
}
}
ObservableList returnList=FXCollections.observableArrayList(所有文件);
退货清单;
}
}

我不明白如何让TableView等待任务完成,而不阻塞整个JavaFX线程,这基本上违背了任务的目的。我希望它能够实时显示进度,只需显示当时添加的曲目数量。

您可以在列表中添加一个监听器

         pjesme.addListener(new ListChangeListener<Track>() {
            @Override
        public void onChanged(ListChangeListener.Change<? extends Track> c) {
            /* Do your Stuff in the gui. Like having the status bar or things */
        }
         });
pjesme.addListener(新的ListChangeListener(){
@凌驾

public void onChanged(ListChangeListener.ChangeJavaFX有两个特定的线程规则:

  • 必须在FX应用程序线程上对UI进行任何更改。不这样做将导致运行时抛出
    IllegalStateException
    s,或者可能使UI处于不一致的状态,可能在将来的任意时间点导致不可预测的行为
  • 任何运行时间较长的代码都应在后台线程上执行。不这样做将导致UI在代码运行时变得无响应
  • 此外,还有一条关于线程的一般规则:

    访问多线程中的可变状态时必须小心。特别是,应确保给定线程中的操作是原子的,并且可能需要特别小心,以确保在一个线程中对数据状态所做的更改对另一个线程可见

    正确处理最后一部分尤其具有挑战性。此外,在UI环境中使用后台线程时,您几乎总是希望在后台线程和UI线程之间共享过程的结果,有时还希望共享过程中计算的数据。因为这很具挑战性,JavaFX提供了
    Task
    类(以及一些其他相关类),负责处理其中更复杂的部分,以覆盖大多数用例

    特别是,
    任务
    类公开了各种属性(包括
    状态
    进度
    消息
    ,以及
    ),以及线程安全的
    updateXXX
    方法。从任何线程调用更新方法都是安全的,并且都将确保在UI线程上更新属性,并限制对它们的更新数量(如果更新发生在UI更新时间内,则基本上将更新合并在一起)。这意味着可以安全地从后台线程调用
    update
    方法,并观察UI线程中的属性。此外,您可以随时调用这些方法,而不会“淹没”UI线程并使其失去响应

    Task
    类还公开用于从一种状态转换到另一种状态的处理程序,例如
    setOnSucceeded
    (在任务正常完成时调用)和
    setOnFailed
    (在任务引发异常时调用)。这些处理程序也在FX应用程序线程上处理

    您的任务子类可以:

  • 使用message属性更新已处理的曲目数
  • 返回生成的轨迹列表
  • 从UI代码中,您可以将标签文本绑定到消息属性。您还可以使用on
    onSucceeded
    处理程序在任务完成时更新UI

    为了确保线程之间不共享可变状态(由
    任务
    机制正确管理的状态除外),您应该正确封装类。这意味着不公开任务本身操纵的任何状态。您的任何状态都不应该是
    静态
    (无论如何,没有明显的理由让你这么做)

    因此,我将把任务写如下:

    public class PathGetter extends Task<ObservableList<Track>> {
    
        private final boolean getSubDirs;
        private final String fullPath;
    
        public PathGetter(String path, boolean getSubDirs) {
            fullPath = path;
            this.getSubDirs = getSubDirs ;
        }
    
        public static boolean isSupported (File f){
            String fileName = f.toString();
            //supported file types
            return fileName.endsWith(".flac") 
                   || fileName.endsWith(".mp3")  
                   || fileName.endsWith(".aiff") 
                   || fileName.endsWith(".ogg") 
                   || fileName.endsWith(".mp4") ;
        }
    
        @Override
        protected ObservableList<Track> call() throws Exception {
    
            List<Track> allFiles = new ArrayList<Track>();
            Queue<File> dirs = new LinkedList<File>();
            dirs.add(new File(fullPath));
            while (!dirs.isEmpty()) {
                for (File f : dirs.poll().listFiles()) {
                    if (f.isDirectory() && getSubDirs) {
                        dirs.add(f);
                    } else if (f.isFile() && isSupported(f)) {
                        allFiles.add(new Track(f));
                        updateMessage("Number of tracks processed: "+allFiles.size());
                    }
                }
            }
            ObservableList<Track> returnList = FXCollections.observableArrayList(allFiles);
            return returnList;
        }
    }
    

    但现在的问题是,您正在一个线程中修改列表,并在另一个线程中观察它。
    ObservableList
    的实现不是线程安全的……因此,要实现这一点,您需要使用
    Platform.runLater()
    将修改列表的所有调用包装到任务中。例如:“警告:不要将可变状态传递给任务,然后从后台线程对其进行操作。”。问题:您似乎正在跟踪我处理的文件计数
    public class PathGetter extends Task<ObservableList<Track>> {
    
        private final boolean getSubDirs;
        private final String fullPath;
    
        public PathGetter(String path, boolean getSubDirs) {
            fullPath = path;
            this.getSubDirs = getSubDirs ;
        }
    
        public static boolean isSupported (File f){
            String fileName = f.toString();
            //supported file types
            return fileName.endsWith(".flac") 
                   || fileName.endsWith(".mp3")  
                   || fileName.endsWith(".aiff") 
                   || fileName.endsWith(".ogg") 
                   || fileName.endsWith(".mp4") ;
        }
    
        @Override
        protected ObservableList<Track> call() throws Exception {
    
            List<Track> allFiles = new ArrayList<Track>();
            Queue<File> dirs = new LinkedList<File>();
            dirs.add(new File(fullPath));
            while (!dirs.isEmpty()) {
                for (File f : dirs.poll().listFiles()) {
                    if (f.isDirectory() && getSubDirs) {
                        dirs.add(f);
                    } else if (f.isFile() && isSupported(f)) {
                        allFiles.add(new Track(f));
                        updateMessage("Number of tracks processed: "+allFiles.size());
                    }
                }
            }
            ObservableList<Track> returnList = FXCollections.observableArrayList(allFiles);
            return returnList;
        }
    }
    
    browseButton.setOnAction(event -> {
        File dir = folderPicker.showDialog(bwindow);
        if(dir != null){
            directoryLocation.setText(String.valueOf(dir));
            Label label = new Label("Loading Tracks");
            bottom.getChildren().add(label);
    
            PathGetter task = new PathGetter(directoryLocation.getText(), Browser.chSubDirs.isSelected());
            Thread th = new Thread(task);
    
            // keep label showing message from task:
            label.textProperty().bind(task.messageProperty());
    
            task.setOnSucceeded(e -> {
                selection.setItems(task.getValue());
                chSelAll.setDisable(false);
                chSelIncomplete.setDisable(false);
                chSelNoCover.setDisable(false);
            });
    
            task.setOnFailed(e -> {
                // handle exception ...
    
                // and log it
                task.getException().printStackTrace();
            });
    
            chSelAll.setDisable(true);
            chSelIncomplete.setDisable(true);
            chSelNoCover.setDisable(true);
    
            // make sure thread doesn't prevent application exit:
            th.setDaemon(true);
    
            // set it going:
            th.start();
    
        }
    
    });