144 votes

Comment savoir si d'autres discussions sont terminées?

J'ai un objet avec une méthode appelée StartDownload (), qui démarre trois threads. Comment puis-je recevoir une notification lorsque chaque thread a fini de s'exécuter?

Existe-t-il un moyen de savoir si un (ou tous) des threads est terminé ou est toujours en cours d'exécution?

250voto

Eddie Points 27755

Il y a un certain nombre de façons dont vous pouvez faire ceci:

  1. L'Utilisation De Threads.join() dans votre thread principal à attendre dans un blocage de la mode pour chaque Thread au complet, ou
  2. Vérifier Le Fil De Discussion.isAlive() dans un mode d'interrogation -- généralement déconseillée -- attendre jusqu'à ce que chaque Thread est terminé, ou
  3. Peu orthodoxes, pour chaque Thread en question, appelez setUncaughtExceptionHandler pour appeler une méthode de l'objet et du programme de chaque Thread pour lancer une Exception non interceptée lorsqu'elle est terminée, ou
  4. Utiliser des verrous ou des synchroniseurs ou des mécanismes de java.util.simultanées, ou
  5. Plus orthodoxe, de créer un écouteur dans votre Thread principal, puis le programme de chacun de vos Fils pour dire à l'auditeur qu'ils ont terminé.

Comment mettre en œuvre l'Idée #5? Eh bien, une façon est d'abord de créer une interface:

public interface ThreadCompleteListener {
    void notifyOfThreadComplete(final Thread thread);
}

puis créez la classe suivante:

public abstract class NotifyingThread extends Thread {
  private final Set<ThreadCompleteListener> listeners
                   = new CopyOnWriteArraySet<ThreadCompleteListener>();
  public final void addListener(final ThreadCompleteListener listener) {
    listeners.add(listener);
  }
  public final void removeListener(final ThreadCompleteListener listener) {
    listeners.remove(listener);
  }
  private final void notifyListeners() {
    for (ThreadCompleteListener listener : listeners) {
      listener.notifyOfThreadComplete(this);
    }
  }
  @Override
  public final void run() {
    try {
      doRun();
    } finally {
      notifyListeners();
    }
  }
  public abstract void doRun();
}

et puis chacun de vos Fils va étendre NotifyingThread et au lieu de la mise en œuvre de run() il va mettre en oeuvre doRun(). Ainsi, lorsqu'ils ont terminé, ils seront automatiquement notifier à toute personne en attente de la notification.

Enfin, dans votre classe principale -- celui qui commence tous les Threads (ou au moins l'objet en attente de notification) -- modifier la classe d' implement ThreadCompleteListener et immédiatement après la création de chaque Thread s'ajouter à la liste des auditeurs:

NotifyingThread thread1 = new OneOfYourThreads();
thread1.addListener(this); // add ourselves as a listener
thread1.start();           // Start the Thread

puis, comme chaque Thread s'arrête, votre notifyOfThreadComplete méthode sera invoquée avec le Fil de l'instance qui vient de se terminer (ou écrasé).

Notez que le mieux serait d' implements Runnable plutôt que d' extends Thread pour NotifyingThread comme l'extension de Fil est généralement déconseillé dans le nouveau code. Mais je suis de codage à votre question. Si vous modifiez l' NotifyingThread classe pour implémenter Runnable , alors vous devez changer certaines de vos code qui gère les Threads, ce qui est assez simple à faire.

14voto

Boris Pavlović Points 22207

Solution à l'aide de CyclicBarrier

public class Downloader {
  private CyclicBarrier barrier;
  private final static int NUMBER_OF_DOWNLOADING_THREADS;

  private DownloadingThread extends Thread {
    private final String url;
    public DownloadingThread(String url) {
      super();
      this.url = url;
    }
    @Override
    public void run() {
      barrier.await(); // label1
      download(url);
      barrier.await(); // label2
    }
  }
  public class startDownload() {
    // plus one for the main thread of execution
    barrier = new CyclicBarrier(NUMBER_OF_DOWNLOADING_THREADS + 1); // label0
    for (int i = 0; i < NUMBER_OF_DOWNLOADING_THREADS; i++) {
      new DownloadingThread("http://www.flickr.com/someUser/pic" + i + ".jpg").start();
    }
    barrier.await(); // label3
    displayMessage("Please wait...");
    barrier.await(); // label4
    displayMessage("Finished");
  }
}

label0 - cyclique de la barrière est créé avec le nombre de parties égal au nombre de l'exécution de threads, plus un pour le thread principal d'exécution (dans lequel startDownload() est en cours d'exécution)

l'étiquette 1 - n-ième DownloadingThread entre dans la salle d'attente

étiquette 3 - NUMBER_OF_DOWNLOADING_THREADS avez entré dans la salle d'attente. Thread principal d'exécution des rejets à commencer à faire de leur téléchargement des emplois plus ou moins en même temps

étiquette 4 - thread principal d'exécution entre dans la salle d'attente. C'est la plus délicate de la partie du code à comprendre. Il n'a pas d'importance thread qui va entrer dans la salle d'attente pour la deuxième fois. Il est important que tout ce thread entre dans la salle dernier s'assure que tous les autres de télécharger les threads ont fini leur téléchargement d'emplois.

étiquette 2 - n-ième DownloadingThread a fini son téléchargement du travail et entre dans la salle d'attente. Si c'est le dernier, c'est à dire déjà NUMBER_OF_DOWNLOADING_THREADS la saisie, y compris le thread principal de l'exécution, le thread principal continuera son exécution que lorsque tous les autres threads avez terminé le téléchargement.

9voto

broc.seib Points 1767

Vous devriez vraiment préférer une solution qui utilise java.util.concurrent. Trouver et lire Josh Bloch et/ou de Brian Goetz sur le sujet.

Si vous n'êtes pas à l'aide de java.util.concurrent.* et assument la responsabilité de l'utilisation de Threads directement, alors vous devriez probablement utiliser join() de savoir quand un thread est fait. Ici est super simple mécanisme de Rappel. Tout d'abord étendre le Runnable interface d'avoir un rappel:

public interface CallbackRunnable extends Runnable {
    public void callback();
}

Ensuite, faire un Exécuteur testamentaire qui exécutera votre exécutable et vous rappeler quand il est fait.

public class CallbackExecutor implements Executor {

    @Override
    public void execute(final Runnable r) {
        final Thread runner = new Thread(r);
        runner.start();
        if ( r instanceof CallbackRunnable ) {
            // create a thread to perform the callback
            Thread callerbacker = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        // block until the running thread is done
                        runner.join();
                        ((CallbackRunnable)r).callback();
                    }
                    catch ( InterruptedException e ) {
                        // someone doesn't want us running. ok, maybe we give up.
                    }
                }
            });
            callerbacker.start();
        }
    }

}

L'autre sorte de chose évidente à ajouter à votre CallbackRunnable interface est un moyen de gérer toutes les exceptions, donc peut-être mettre un public void uncaughtException(Throwable e); ligne là-bas et à votre exécuteur testamentaire, d'installer un Fil.UncaughtExceptionHandler de vous envoyer à l'interface de la méthode.

Mais le faire tous ça commence vraiment à sentir comme java.util.concurrent.Callable. Vous devriez vraiment regarder à l'aide de java.util.concurrent si votre projet le permet.

4voto

Jonathan Allen Points 23540

Voulez-vous attendre qu'ils finissent? Si c'est le cas, utilisez la méthode Join.

Il y a aussi la propriété isAlive si vous voulez juste la vérifier.

4voto

Miquel Points 2184

Vous pouvez interroger l'instance de thread avec getState () qui renvoie une instance de l'énumération Thread.State avec l'une des valeurs suivantes:

 *  NEW
  A thread that has not yet started is in this state.
* RUNNABLE
  A thread executing in the Java virtual machine is in this state.
* BLOCKED
  A thread that is blocked waiting for a monitor lock is in this state.
* WAITING
  A thread that is waiting indefinitely for another thread to perform a particular action is in this state.
* TIMED_WAITING
  A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state.
* TERMINATED
  A thread that has exited is in this state.
 

Cependant, je pense que ce serait une meilleure conception d'avoir un thread principal qui attend que les 3 enfants soient terminés, le maître continuerait alors l'exécution une fois les 3 autres terminés.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X