Как мне кодировать многопоточность, чтобы ускорить выполнение тяжелой, повторяющейся задачи? - PullRequest
0 голосов
/ 30 апреля 2019

У меня есть приложение Swing, которое довольно медленно запускается, потому что оно должно загрузить тысячу картинок в графический интерфейс. Для запуска требуется 10 секунд.

Это однопоточное приложение, как я могу кодировать многопоточность, чтобы ускорить задачу? Следующий код находится в цикле for из 1000 итераций.

    ImageIcon icon = new ImageIcon(Files.readAllBytes(coverFile.toPath()));
    // ImageIcon icon = createImageIcon(coverFile);
    JLabel label = null;
    if (coverCount % 2 == 0) {
     label = createLabel(coverFile, movieFile, icon, SwingConstants.LEFT);
    } else {
     label = createLabel(coverFile, movieFile, icon, SwingConstants.CENTER);
    }
    box.add(label);

Изображения загружаются и помещаются в коробку последовательно. У меня две трудности, если я хочу сделать это многопоточным

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

Спасибо.

Ответы [ 3 ]

2 голосов
/ 30 апреля 2019

Как поток возвращает значение родительскому элементу

Используйте механизм обратного вызова. Для Swing это означало бы использование SwingWorker и уведомление GUI о завершении потока либо в методе done() работника, либо путем добавления PropertyChangeListener к работнику, прослушивающего свойство "state" работника, когда оно меняется на SwingWorker.StateValue.DONE

Как добиться неблокирующего обратного вызова, который последовательно добавляет изображение в поле

SwingWorker имеет пару методов публикации / процесса, которая позволяет последовательно отправлять данные из фонового потока через метод публикации, а затем последовательно обрабатывать данные в потоке событий в методе процесса. Для этого требуется использовать SwingWorker<VOID, Image> или SwingWorker<VOID, Icon> или что-то подобное, 2-й универсальный параметр, указывающий тип объекта, отправляемого через этот механизм.

Например:

public class MyWorker extends SwingWorker<Void, Icon> {
    @Override
    protected Void doInBackground() throws Exception {
        boolean done = false;
        while (!done) {
            // TODO: create some_input here -- possibly a URL or File
            Image image = ImageIO.read(some_input);
            Icon icon = new ImageIcon(image);
            publish(icon);

            // TODO: set done here to true when we ARE done
        }
        return null;
    }

    @Override
    protected void process(List<Icon> chunks) {
        for (Icon icon : chunks) {
            // do something with the icon here
            // on the event thread
        }
    }
}

И использовать его в графическом интерфейсе:

// may need constructor to pass GUI into worker
final MyWorker myWorker = new MyWorker();
myWorker.addPropertyChangeListener(evt -> {
    if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
        // We're done!
        // call get to trap errors
        try {
            myWorker.get();
        } catch (InterruptedException | ExecutionException e) {
            // TODO: real error handling needed here
            e.printStackTrace();
        }
    }
});
myWorker.execute(); // start worker in a background thread

Подробнее о параллелизме на Swing читайте в Урок: параллелизм на Swing

0 голосов
/ 30 апреля 2019

Если вы действительно хотите загрузить все 1000 изображений:

Достаточно использовать один фоновый поток, чтобы не замедлять основной поток цикла Swing Event.

Создайте пользовательский класс, который реализует runnable и имеет ссылки на весь контекст для выполнения работы. Вот так:

public static class IconLoader implements Runnable{
    private List<File> movies;
    private File coverFile;
    private JPanel box;
    public IconLoader(JPanel box, File coverFile, List<File> movies) {
        this.box = box;
        this.coverFile = coverFile;
        this.movies = movies;
    }

    @Override
    public void run() {

        for(int coverCount=0;coverCount<movies.size();coverCount++) {
            try {
                final JLabel label;
                File movieFile = movies.get(coverCount);
                ImageIcon icon = new ImageIcon(Files.readAllBytes(coverFile.toPath()));
                // ImageIcon icon = createImageIcon(coverFile);

                if (coverCount % 2 == 0) {
                 label = createLabel(coverFile, movieFile, icon, SwingConstants.LEFT);
                } else {
                 label = createLabel(coverFile, movieFile, icon, SwingConstants.CENTER);
                }

                SwingUtilities.invokeLater( new Runnable() {
                    @Override
                    public void run() {
                        box.add(label);
                    }
                });
            }catch(IOException e) {
                e.printStackTrace();
            }
        }
    }

    private JLabel createLabel(File coverFile, File movieFile, ImageIcon icon, int direction) {
        //Create the label and return
        return null;
    }
}

Затем запустите процесс загрузки во время инициализации приложения, передавая runnable новому потоку и запуская поток. Вот так:

new Thread( new IconLoader(box, coverFile, movies) ).start();
0 голосов
/ 30 апреля 2019

Многопоточность ускорит работу приложения, но я думаю, что ленивая загрузка - лучший подход (вы можете сделать и то, и другое).Вы не можете отображать все эти изображения одновременно, поэтому я предлагаю вам загружать изображения, которые будут видны в начале, и после этого загружать изображение по мере необходимости, это значительно повысит вашу производительность и использует меньше памяти / ресурсов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...