Qual é a melhor maneira de retornar variáveis de um syncExec?
-
09-06-2019 - |
Pergunta
Em meu aplicativo SWT Java, geralmente desejo retornar informações de dentro de uma chamada Display.syncExec().A melhor maneira que encontrei até agora de fazer isso é:
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)) { /* ... */ }
Acho que isso é permitido porque ArrayList é seguro para threads, mas existe um contêiner melhor que eu deveria usar ou uma maneira mais fácil?
Solução
ArrayList
não é thread-safe.Você pode obter um thread-safe List
com Collections.synchronizedList
.Entretanto, é muito mais simples usar um AtomicInteger
no seu caso ou AtomicReference
num caso mais geral.
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()) { /* ... */ }
Outras dicas
Acabei de resolver esse problema e minha primeira tentativa foi semelhante - matriz ou lista de itens do tipo desejado.Mas depois de um tempo eu inventei algo assim:
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
É muito mais organizado e remove variáveis redundantes.
POR FALAR NISSO.Minha primeira tentativa foi usar o UIThreadRunnable, mas eu não queria a dependência do SWTBot, então abandonei essa solução.Depois que fiz minha própria solução, descobri que eles usam um trabalho semelhante lá.
ArrayList é não discussão segura.Do relevante Javadoc:
Observe que esta implementação não é sincronizada.Se vários threads acessarem uma instância do Arraylist simultaneamente e pelo menos um dos threads modifica a lista estruturalmente, ela deve ser sincronizada externamente.
Se você precisar de uma implementação thread-safe de List, há (pelo menos) duas fornecidas no JDK:CopyOnWriteArrayList e vetor.
Você poderia usar um array Integer[1] para torná-lo mais conciso, mas não acho que ele possa atualizar diretamente uma variável não final de dentro de uma classe interna anônima.
final Integer[] result = new Integer[1];
Achei que você deveria declarar os resultados como finais (mas essa mudança não afetaria seu código).Como o Thread atual é bloqueado até que o Thread interno seja concluído, não acho que você precise se preocupar com a sincronização (mas pode ser necessário violar a variável para ver o resultado).
Se isso acontecer com frequência, é melhor usar o modelo de assinatura/notificação entre seu processo e visualização.Sua visualização se inscreve no evento que deve acionar aquela caixa de mensagem e é notificada quando as condições são atendidas.