Какой наилучший способ вернуть переменные из syncExec?
-
09-06-2019 - |
Вопрос
В моем Java-приложении SWT я часто хочу возвращать информацию изнутри вызова Display.syncExec().Лучший способ, который я нашел до сих пор, чтобы сделать это, - это:
final ArrayList<Integer> result = new ArrayList<Integer>();
GUI.display().syncExec(new Runnable(){ public void run() {
MessageBox mb = /* ... */;
/* set up messagebox */
result.add(mb.open());
}});
if (SWT.OK == result.get(0)) { /* ... */ }
Я думаю, что это разрешено, потому что ArrayList потокобезопасен, но есть ли лучший контейнер, который я должен использовать, или вообще более простой способ?
Решение
ArrayList
не является потокобезопасным.Вы можете получить потокобезопасный List
с Collections.synchronizedList
.Однако гораздо проще использовать AtomicInteger
в вашем случае или AtomicReference
в более общем случае.
final AtomicInteger resultAtomicInteger = new AtomicInteger();
Display.getCurrent().syncExec(new Runnable() {
public void run() {
MessageBox mb = /* ... */;
/* set up messagebox */
resultAtomicInteger.set(mb.open());
}});
if (SWT.OK == resultAtomicInteger.get()) { /* ... */ }
Другие советы
Я только что решил эту проблему, и моя первая попытка была аналогичной - массив или список элементов нужного типа.Но через некоторое время я придумал что-то вроде этого:
abstract class MyRunnable<T> implements Runnable{
T result;
}
MyRunnable<Integer> runBlock = new MyRunnable<Integer>(){
MessageBox mb = /* ... */;
/* set up messagebox */
result = mb.open();
}
GUI.display().syncExec(runBlock);
runBlock.result; //holds a result Integer
Это намного опрятнее и удаляет избыточные переменные.
Кстати.Моей действительно первой попыткой было использовать UIThreadRunnable, но мне не нужна была зависимость от SWTBot, поэтому я отказался от этого решения.После того, как я создал свое собственное решение, я узнал, что они используют аналогичную работу и там.
ArrayList - это нет потокобезопасен.Из соответствующих Javadoc:
Обратите внимание, что эта реализация не является синхронизированы.Если несколько потоков обращаются к экземпляру ArrayList одновременно, и хотя бы один из потоков изменяет список структурно, он должен быть синхронизирован извне.
Если вам нужна потокобезопасная реализация List, в JDK есть (как минимум) две из них:CopyOnWriteArrayList и вектор.
Вы могли бы использовать массив Integer [1], чтобы сделать его более кратким, но я не думаю, что он может напрямую обновлять неокончательную переменную изнутри анонимного внутреннего класса.
final Integer[] result = new Integer[1];
Я думал, что вы должны были объявить результаты как окончательные (но это изменение не повлияет на ваш код).Поскольку текущий поток блокируется до тех пор, пока внутренний поток не будет завершен, я не думаю, что вам нужно беспокоиться о синхронизации (но вам может потребоваться заставить переменную нарушать, чтобы вы увидели результат).
Если это происходит часто, вам лучше использовать модель подписки / уведомления между вашим процессом и представлением.Ваше представление подписывается на событие, которое должно вызвать это окно сообщения, и получает уведомление при выполнении условий.