Domanda

Ho fatto una domanda di swing ma c'è un problema in questo che è il seguente:

Ho avviato un thread SwingWorker chiamato " Thread-Main " dal thread di invio eventi e hanno passato un riferimento JLabel della GUI al " Thread-Main " ;.

Ora ho iniziato 10 discussioni dalla " Discussione principale " ;.

Ora voglio che tutti i 10 thread aggiornino JLabel.

Come posso farlo?

Qualcuno mi ha detto che posso farlo facendo prima tutte le 10 sottoclassi di thread di SwingWorker e poi chiamando il metodo publishing (" ") e passando la stringa in quel " pubblicare quot &; e quindi raccogliere tutte le stringhe pubblicate con il seguente metodo in " 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 
}
  1. L'approccio sopra è corretto per farlo?
  2. I 10 thread dovrebbero essere sottoclassi di SwingWorker?
  3. C'è un altro modo per farlo?
È stato utile?

Soluzione

  1. No - Questo è l'approccio errato se vuoi controllare tu stesso il numero di thread . Perché? Perché se guardi il SwingWorker codice vedrai che usa un ThreadPoolExecutor che contiene internamente un massimo di 10 thread. Se si avvia contemporaneamente un numero di doInBackground() verranno eseguiti tutti utilizzando questo esecutore. Tuttavia, non hai alcun controllo diretto sul fatto che i thread in background vengano eseguiti in parallelo .
  2. Vedi punto 1.
  3. La mia soluzione raccomandata sarebbe:

    • Crea un singolo ExecutorService.
    • Nel metodo CountDownLatch, avvia 10 thread direttamente o utilizzando un CompletionService.
    • Utilizzare un Callable<String> o String per sincronizzare tra il thread principale (ovvero JLabel thread in background) e i thread di lavoro.

Esempio

Definire il numero di thread di lavoro da invocare e dichiarare JLabel da aggiornare.

final int nThreads = 10;
JLabel myLbl = new JLabel();

Definisci l'unità di lavoro che desideriamo eseguire come done(). Il Void risultato verrà utilizzato per aggiornare process(String... chunks).

private static class MyCallable implements Callable<String> {
  public String call() { ... }
}

Ora avvia un <=>, che a sua volta darà il via a più lavoratori paralleli per eseguire qualsiasi elaborazione. Il lavoratore non restituirà un risultato tramite la chiamata a <=> (quindi il <=> tipo) ma eseguirà marshall intermedio <=> risultati sul thread Swing chiamando <=>.

new SwingWorker<Void, String>() {
  // See method definitions below.
}.execute();

Definisci <=> per dare il via ai thread di lavoro e bloccare per ogni risultato usando un <=>.

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);
  }
}

Ora implementiamo <=> per aggiornare semplicemente <=> quando chiamato.

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]);
  }
}

Infine, eseguiamo l'override di <=> per eseguire il marshalling di eventuali eccezioni sul thread 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.
  }
}

Altri suggerimenti

Forse un approccio più semplice è racchiudere il codice che aggiorna la GUI in un metodo SwingUtilities.invokeLater (...).

Modifica: nei tuoi singoli thread ogni volta che vuoi aggiornare l'etichetta che fai:

SwingUtilities.invokeLater(new Runnable()
{
    public void run()
    {
        label.setText(...);
    }
});

perché hai bisogno di 10 discussioni? perché un thread aggiuntivo non funziona?

la vera domanda: qual è il problema che stai cercando di risolvere?

per rispondere alle tue domande dirette:

1) sì, questo è l'approccio corretto 2) sì, i thread dovrebbero essere SwingWorkers (se stai usando netbeans, puoi anche usare Tasks, che sono anche sottoclassi di SwingWorker)

3) se vuoi avere un thread separato da edt; allora devi usare un swingworker; quindi questo è il modo per farlo.

buona fortuna!

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top