Question

J'ai créé une application Swings, mais il y a un problème dans ce qui suit:

J'ai lancé un thread SwingWorker nommé & "Thread-Main &"; à partir du thread d’affectation d’événements et ont passé une référence JLabel de l’interface graphique au & "Thread-Main &";.

Maintenant, j'ai lancé 10 threads à partir du & "Thread-Main &";

.

Maintenant, je veux que les 10 threads mettent à jour le JLabel.

Comment puis-je faire cela?

Quelqu'un m'a dit que je pouvais le faire en créant d'abord les 10 sous-classes de threads de SwingWorker, puis en appelant la méthode publish (& "; &") et en passant la chaîne dans ce & "; publier " puis recueillez toutes les chaînes publiées selon la méthode suivante dans & "; 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. Est-ce que l'approche ci-dessus est correcte pour le faire?
  2. Les 10 threads doivent-ils être des sous-classes de SwingWorker?
  3. Y at-il un autre moyen de le faire?
Était-ce utile?

La solution

  1. Non : il s'agit d'une approche incorrecte si vous souhaitez contrôler vous-même le nombre de sujets . Pourquoi? Parce que si vous regardez le code SwingWorker, vous verrez qu'il utilise un ThreadPoolExecutor en interne contenant au maximum 10 threads. Si vous lancez plusieurs doInBackground() en même temps, ils s'exécutent tous avec cet exécuteur. Cependant, vous n'avez aucun contrôle direct sur l'exécution parallèle des threads d'arrière-plan .
  2. voir le point 1.
  3. Ma solution recommandée serait:

    • Créer un seul ExecutorService.
    • Dans la CountDownLatch méthode, démarrez 10 threads directement ou à l'aide d'un CompletionService.
    • Utilisez un Callable<String> ou String pour synchroniser votre thread principal (c.-à-d. JLabel un thread d'arrière-plan) et des threads de travail.

Exemple

Définissez le nombre de threads de travail à appeler et déclarez que le JLabel doit être mis à jour.

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

Définissez l'unité de travail que vous souhaitez exécuter en tant que done(). Le Void résultat sera utilisé pour mettre à jour le process(String... chunks).

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

Lancez maintenant un <=>, qui déclenchera à son tour plusieurs travailleurs parallèles pour effectuer tout traitement. Le travailleur ne renverra pas de résultat via l'appel à <=> (d'où le type <=>), mais fera marshaller les résultats intermédiaires <=> sur le fil Swing en appelant <=>.

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

Définissez <=> pour lancer les threads de travail et bloquez chaque résultat à l'aide d'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);
  }
}

Maintenant, nous implémentons <=> simplement pour mettre à jour le <=> lorsqu'il est appelé.

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

Enfin, nous substituons <=> pour regrouper toutes les exceptions sur le fil de discussion 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.
  }
}

Autres conseils

Une approche plus simple consiste peut-être à envelopper le code qui met à jour l'interface graphique dans une méthode SwingUtilities.invokeLater (...).

Modifier: dans vos threads individuels chaque fois que vous souhaitez mettre à jour le libellé que vous effectuez:

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

pourquoi avez-vous besoin de 10 threads? pourquoi un fil supplémentaire ne le ferait-il pas?

la vraie question: quel est le problème que vous essayez de résoudre?

pour répondre à vos questions directes:

1) oui, c'est la bonne approche 2) oui, les discussions devraient être SwingWorkers (si vous utilisez netbeans, vous pouvez également utiliser des tâches, qui sont également des sous-classes de SwingWorker)

3) si vous voulez avoir un thread séparé de l'edt; alors vous devez utiliser un swingworker; c'est donc la manière de le faire.

bonne chance!

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top