不知道还有没有其他好的方法可以达到我想要的结果,谢谢。
我有一个需求,根据url,创建多个webview线程,并按顺序执行,比如线程执行完,然后触发线程二执行,以此类推,我使用的是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();
}
在Platform.runLater中的run方法中调用lobject.notifyAll()会报IllegalMonitorStateException。地址处理完之后,我想唤醒下一个线程来执行。
最佳答案
如果需要按顺序执行多个任务,则无需创建多个线程。仅使用单个线程将保证下一个任务仅在前一个任务完成后执行。您还应该考虑使用 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
属性的监听器。您可以通过使用匿名类(而不是我使用的 lambda 表达式)并在changed
方法内调用obs.removeListener(this)
来完成此操作,其中obs
是ObservableValue
参数。
关于java - javafx多线程如何顺序执行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56717080/