Я предлагаю использовать SwingWorker
и воспользоваться методом публикации, чтобы публиковать обновления обратно в EDT. EDT будет применять одно или несколько обновлений, когда это запланировано на следующий раз. Например, предположим, что результатом вашего расчета являются Integer
экземпляры:
// Boolean flag to determine whether background thread should continue to run.
AtomicBoolean running = new AtomicBoolean(true);
new SwingWorker<Void, Integer>() {
public Void doInBackground() {
while (running.get()) {
// Do calculation
Integer result = doCalculation();
publish(result);
}
}
protected void process(List<Integer> chunks) {
// Update UI on EDT with integer results.
}
}
Альтернативный подход заключается в создании одного Runnable
экземпляра, который просто потребляет из Queue
результатов каждый раз, когда он запланирован. Это похоже на то, как SwingWorker
работает под прикрытием, но дает вам немного больший контроль, поскольку вы можете контролировать реализацию Queue
и избегать постоянного удержания одного из SwingWorker
потоков пула.
private final Queue<Integer> resultQueue = Collections.synchronizedList(new LinkedList<Integer>());
// Thread-safe queue to hold results.
// Our re-usable Runnable to be run on the EDT. Consumes all results from the result queue
// before releasing the lock, although could obviously change this to consume up to N results.
Runnable consumer = new Runnable() {
public void run() {
synchronized(resultQueue) {
Integer result;
while ((result = resultQueue.take()) != null) {
// Update UI.
}
}
}
}
...
// Business logic to run on background thread. Conditionally schedules the EDT to run as required.
while (true) {
Integer result = doCalculation();
synchronized(resultQueue) {
boolean scheduleEdt = resultQueue.isEmpty();
// Queue was empty before this result is added so need to schedule the EDT to run again.
resultQueue.add(result);
}
if (scheduleEdt) {
SwingUtilities.invokeLater(consumer);
} else {
System.err.println("EDT already scheduled -> Avoiding unecessary reschedule.");
}
}