Я решил проблему с помощью класса PropertyChangeSupport
. Это обеспечивает потокобезопасные свойства, а также обеспечивает прослушиватели свойств. Больше от здесь .
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
срабатывает только тогда, когда значения old и new отличаются , в противном случае обновление не будет выполняться для слушателей.
Теперь мы создаем класс 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, реализованном так:
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();
}
}
Теперь обычные привязки JavaFX, такие как
progressBar.progressProperty().bind(task.progressProperty())
Полный исходный код можно найти в здесь .