我需要拆分一些线程的工作量,并并行启动,因为它们是独立的。我还想在带有JavaFx的ProgressBar中显示总体进度。这意味着进度条会显示到目前为止每个线程完成的总工作量。
为简单起见,我们可以以这个Counter
类为例
public class Counter implements Runnable {
private int from, to;
public Counter(int from, int to) {
this.from = from;
this.to = to;
}
@Override
public void run() {
for (int i = from; i < to ; i++) {
// Do some heavy operation
// report about progress to the parent
}
// exit thread with status of success or failure
}
}
该类从到作为边界条件。
为了不阻塞用户界面,我使用了一个简单的Task
类
public class MyTask extends Task<Integer> {
int iter;
public MyTask(int iter) {
this.iter = iter;
}
@Override
protected Integer call() throws Exception {
// Simply divide work load to each thread
int workers = 8;
int limit = iter/workers;
int rem = iter % workers;
// Creates a executor
ExecutorService executorService = Executors.newFixedThreadPool(workers);
for (int i = 0; i < workers; i++) {
int start = limit * i;
int end = limit * (i + 1);
if (i == workers - 1) end += rem;
Counter counter = new Counter(start, end);
executorService.submit(counter); // Submit work to be done
}
executorService.shutdown(); // start the execution
// Get their progress, update overall progress to UI
// Stop after all threads finished
}
}
在MyTask
中,我要按照注释中的说明更新UI,并完整填写。 (即每个线程完成的总计数)。
有什么方法可以做到这一点?汇总并行任务的进度并更新UI中的总体进度(不指望已完成线程的数量,这是我要向MyTask报告的每个线程的当前进度)。
答案 0 :(得分:2)
根据并行运行的任务数,您可以简单地将Task
用于Counter
逻辑,并将从侦听器的总体进度更新为这些任务的progress
属性。 / p>
但是,如果这些任务中有太多并行运行,这可能会减慢JavaFX应用程序线程的速度,因为可能有太多Runnable
s等待一次执行。
您可以通过使用synchronized
语句来根据进度差异自行实现更新。为了简单起见,以下代码从javafx应用程序线程开始更新,但是可以将此逻辑移至其他线程而不会引起任何问题:
@Override
public void start(Stage primaryStage) {
ProgressBar progressBar = new ProgressBar();
int workers = 8;
ExecutorService executorService = Executors.newFixedThreadPool(workers);
final int taskCount = 12;
final int elementsPerTask = 50;
final int elementCount = elementsPerTask * taskCount;
ProgressReceiver progressReceiver = new ProgressReceiver() {
private boolean updating = false;
private int progress = 0;
@Override
public void acceptProgress(int oldValue, int newValue) {
synchronized(this) {
progress += newValue - oldValue;
if (!updating) {
updating = true;
Platform.runLater(() -> {
synchronized (this) {
updating = false;
progressBar.setProgress(((double) progress) / elementCount);
}
});
}
}
}
};
for (int i = 0; i < taskCount; i++) {
int start = elementsPerTask * i;
int end = elementsPerTask * (i + 1);
Counter counter = new Counter(start, end, progressReceiver);
executorService.submit(counter);
}
executorService.shutdown();
StackPane root = new StackPane(progressBar);
Scene scene = new Scene(root, 300, 300);
primaryStage.setScene(scene);
primaryStage.show();
}
public interface ProgressReceiver {
void acceptProgress(int oldValue, int newValue);
}
public class Counter implements Runnable {
private final ProgressReceiver progressReceiver;
private final int from, to;
public Counter(int from, int to, ProgressReceiver progressReceiver) {
this.from = from;
this.to = to;
this.progressReceiver = progressReceiver;
}
@Override
public void run() {
for (int i = from; i < to; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
int oldProgress = i - from;
progressReceiver.acceptProgress(oldProgress, oldProgress + 1);
}
// exit thread with status of success or failure
}
}
答案 1 :(得分:0)
我使用PropertyChangeSupport
类解决了该问题。这提供了线程安全的属性,还提供了属性侦听器。来自here的更多内容。
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
public final class ProgressReporter {
private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
private int progress = 0;
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertyChangeSupport.removePropertyChangeListener(listener);
}
public void accumulateProgress(int progress){
this.propertyChangeSupport.firePropertyChange("progress", this.progress, this.progress + progress);
this.progress += progress;
}
public int getProgress() {
return progress;
}
}
现在,通过听ProgressReporter
,我们可以在收到新数据时获得进度。请注意,firePropertyChange
仅在旧和新值不同时触发,否则不会向监听器触发更新。>
现在,我们创建Counter
类来使用此ProgressReporter
public class Counter implements Runnable {
private int id, from, to, sleep;
private ProgressReporter reporter;
public Counter(int id, int from, int to, int sleep, ProgressReporter reporter) {
this.from = from;
this.to = to;
this.sleep = sleep;
this.id = id;
this.reporter = reporter;
System.out.println("Thread #" + id + " started delay=" + sleep);
}
@Override
public void run() {
for (int i = from; i < to ; i++) {
try {
Thread.sleep(sleep);
reporter.accumulateProgress(1); // this will fire an update to listeners
} catch (InterruptedException e){
}
}
System.out.println("Thread #" + id + " is completed");
}
}
现在在由JavaFX线程启动的Task中,实现如下。
public class MyTask extends Task<Integer> {
int iterations;
Random random = new Random();
ProgressReporter reporter = new ProgressReporter();
public MyTask(int iterations) {
this.iterations = iterations;
}
@Override
protected Integer call() throws Exception {
// Simply divide work load to each thread
int workers = 8;
int limit = iterations /workers;
int rem = iterations % workers;
// add a property listener for progress update
reporter.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
updateProgress((int) evt.getNewValue(), iterations);
}
});
// Creates a executor
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < workers; i++) {
int start = limit * i;
int end = limit * (i + 1);
if (i == workers - 1) end += rem;
Counter counter = new Counter(i ,start, end, random.nextInt(1000), reporter);
executorService.submit(counter); // Submit work to be done
}
executorService.shutdown(); // shutdown executor not to accept any more threads
while (!executorService.isTerminated()){
if (isCancelled()){
executorService.shutdownNow(); // stop all the processes immediately
}
}
return reporter.getProgress();
}
}
现在像Javap这样的常用绑定
progressBar.progressProperty().bind(task.progressProperty())
完整的源代码可以在here中找到。