Как многопоточность javafx выполняется последовательно? - PullRequest
0 голосов
/ 22 июня 2019

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

У меня есть требование, согласно URL, создать несколько потоков веб-просмотра и выполнить их впорядок, например выполнение потока, затем запуск выполнения потока два и т. д. Я использую метод synchronized (lobject), но в JAVAfx возникла проблема, код выглядит следующим образом:

public class LockObject {

    public int orderNum = 1;
    public final static int MaxValue=9;

    public LockObject(int orderNum){
        this.orderNum = orderNum;
    }
}

public class DownloadThread extends Thread{

    private LockObject lobject;
    private int printNum =0;
    private String url;

    public DownloadThread(LockObject lobject,int printNum,String url){
        this.lobject=lobject;
        this.printNum = printNum;
        this.url = url;
    }
    @Override
    public void run() {
        synchronized(lobject){
            while(lobject.orderNum <= lobject.MaxValue){
                if(lobject.orderNum == printNum){
                    System.out.print(printNum);
                    Platform.runLater(new Runnable() {
                        @Override
                        public void run() {
                            webView.getEngine().load(url);
                            webView.getEngine().getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() {
                                @Override
                                public void changed(ObservableValue<? extends Worker.State> observable, Worker.State oldValue, Worker.State newValue) {
                                    if (newValue == Worker.State.SUCCEEDED) {
                                        try {
                                            //xxxxx 

                                            // java.lang.IllegalMonitorStateException
                                            lobject.notifyAll();
                                        } catch (Exception e) {
                                            e.printStackTrace();
                                        }
                                    }
                                }
                            });
                        }
                    });
                    lobject.orderNum++;
                    if(lobject.orderNum==downloadThreads.length){
                        saveCsvFile(goodCSVS);
                    }
                    //lobject.notifyAll();  is ok
                }else{
                    try {
                        lobject.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

Место звонка

private DownloadThread[] downloadThreads;
LockObject lobject = new LockObject(1);
downloadThreads = new DownloadThread[tableView.getItems().size()];
for (int i = 0; i < tableView.getItems().size(); i++) {
    UrlModel item = tableView.getItems().get(i);
    downloadThreads[i] = new DownloadThread(lobject,tableView.getItems().size()-i,item.getLink());
    downloadThreads[i].start();
}

Вызов lobject.notifyAll () в методе run в Platform.runLater выдаст исключение IllegalMonitorStateException.После обработки адреса я хочу разбудить следующий поток для выполнения.

1 Ответ

2 голосов
/ 22 июня 2019

Если вам нужно выполнить несколько задач по порядку, нет необходимости создавать несколько потоков. Использование одного потока гарантирует, что следующая задача будет выполнена только после завершения предыдущей. Вам также следует рассмотреть возможность использования CountDownLatch вместо синхронизации на объекте.

ExecutorService executor = Executors.newSingleThreadExecutor();
try {
    for (UrlModel model : tableView.getItems()) {
        executor.submit(() -> {
            CountDownLatch latch = new CountDownLatch(1);
            Platform.runLater(() -> {
                engine.load(model.getLink())
                engine.getLoadWorker().runningProperty().addListener((obs, ov, nv) -> {
                    if (!nv) {
                        latch.countDown();
                    }
                });
            });
            latch.await();
            // do whatever needs to happen after the WebEngine finishes loading
            return null; // using #submit(Callable) and Callable needs to return something
        });
    }
} finally {
    executor.shutdown();
}

Некоторые заметки:

  • Вы можете не создавать ExecutorService, если в таблице нет элементов для обработки. То есть, если вы не используете один и тот же ExecutorService каждый раз.
  • Если вы повторно используете ExecutorService, не звоните shutdown().
  • Этот ExecutorService использует нити демона. Вы можете настроить это, указав ThreadFactory, который создает потоки демона.
  • Я добавил слушателя в свойство Worker#running вместо свойства status, чтобы упростить гарантию, что countDown() вызывается независимо от состояния терминала нагрузки (т. Е. SUCCEEDED, CANCELLED или FAILED).
  • Вы можете удалить прослушиватель, добавленный к свойству Worker, когда он будет завершен. Вы можете сделать это, используя анонимный класс (а не лямбда-выражение, которое я использовал) и вызвав obs.removeListener(this) внутри метода changed, где obs - аргумент ObservableValue.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...