Problema no javax.swing.SwingWorker
-
06-07-2019 - |
Pergunta
Eu fiz uma aplicação oscilações, mas há um problema no que é a seguinte:
I iniciaram um fio SwingWorker chamado "Thread-Main" a partir do Tópico Despacho Evento e passaram uma referência JLabel da GUI para o "Thread-Main".
Agora eu comecei 10 tópicos do "Thread-Main".
Agora, quero que todos os 10 threads deve atualizar o JLabel.
Como posso fazer isso?
Alguém me disse que eu posso fazer isso por primeira tomada de todas as 10 linhas subclasses de SwingWorker e, em seguida, chamar a publicar ( "") método e passando a cadeia em que "publicar" método e, em seguida, recolher todas as cordas publicados pela seguinte método em "thread-Main"
@Override
protected void process(List<String> labelStrings) {
String count = labelStrings.get(labelStrings.size() - 1);
label.setText(count); // label is a reference to a label in the GUI
}
- É a abordagem acima é correto fazer isso?
- Caso os 10 threads ser subclasses de SwingWorker?
- Existe alguma outra maneira de fazer isso?
Solução
- Não - Esta é a abordagem incorreta se você quiser controlar o número de threads si mesmo. Por quê? Porque se você olhar o código
SwingWorker
você verá que ele usa umaThreadPoolExecutor
contendo internamente um máximo de 10 linhas. Se você lançar uma série deSwingWorker
é simultaneamente todos eles vão correr usando este executor. No entanto, você não tem controle direto sobre se os threads em segundo plano são executadas em paralelo . - Ver ponto 1.
-
A minha solução recomendada seria:
- Criar um único
SwingWorker
. - No pontapé método
doInBackground()
off 10 threads diretamente ou por meio de umExecutorService
. - Use um
CountDownLatch
ouCompletionService
a sincronizar entre o master thread (ou sejaSwingWorker
discussão de fundo) e segmentos de trabalho.
- Criar um único
Defina o número de threads de trabalho para invocar e declarar o JLabel para ser atualizado.
final int nThreads = 10;
JLabel myLbl = new JLabel();
Defina a unidade de trabalho que deseja executar como um Callable<String>
. O resultado String
será usado para atualizar o JLabel
.
private static class MyCallable implements Callable<String> {
public String call() { ... }
}
Agora lançar uma SwingWorker
, que por sua vez pontapé de saída vários trabalhadores paralelos para fazer qualquer processamento. O trabalhador não retornará um resultado via a chamada para done()
(daí o tipo Void
), mas marshall resultados String
intermediário de volta para o segmento balanço chamando process(String... chunks)
.
new SwingWorker<Void, String>() {
// See method definitions below.
}.execute();
Definir doInBackground()
para lançar os threads de trabalho e de bloco para cada resultado utilizando uma CompletionService
.
public Void doInBackground() throws Exception {
// Define executor service containing exactly nThreads threads.
ExecutorService execService = Executors.newFixedThreadPool(nThreads);
// Define completion service that will contain the processing results.
CompletionService compService = new ExecutorCompletionService(execService);
// Submit work to thread pool using the CompletionService. Future<String>
// instances will be added to the completion service's internal queue until complete.
for (int i=0; i<nThreads; ++i) {
compService.submit(new MyCallable());
}
// Take results from each worker as they appear and publish back to Swing thread.
String result;
while ((result = compService.take().get()) != null) {
publish(result);
}
}
Agora vamos implementar process(String... chunks)
simplesmente atualizar o JLabel
quando chamado.
public void process(String... chunks) {
if (chunks.length > 0) {
// Update label with last value in chunks in case multiple results arrive together.
myLbl.setText(chunks[chunks.length - 1]);
}
}
Finalmente, substituir done()
para marshall quaisquer excepções de volta para o segmento Swing.
public void done() {
try {
get(); // Will return null (as per Void type) but will also propagate exceptions.
} catch(Exception ex) {
JOptionPane.show ... // Show error in dialog.
}
}
Outras dicas
Talvez uma abordagem mais fácil é para embrulhar o código que atualiza a GUI em um método SwingUtilities.invokeLater (...).
Edit: Em seus segmentos individuais sempre que você deseja atualizar o rótulo você faz:
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
label.setText(...);
}
});
Por que você precisa de 10 tópicos? por um fio adicional não vai fazer?
a verdadeira questão: qual é o problema que você está tentando resolver
para responder às suas perguntas diretas:
1) Sim, essa é a abordagem correta 2) Sim, os fios devem ser SwingWorkers (se você estiver usando o NetBeans, você também pode usar Tarefas, que são subclasses de SwingWorker também)
3) se você quiser ter um segmento separado do BRT; então você precisa usar um SwingWorker; de modo que é a maneira para fazê-lo.
boa sorte!