J'écris une application qui a 5 threads qui obtiennent des informations de Web simultanément et remplissent 5 champs différents dans une classe de mémoire tampon.
Je dois valider les données du tampon et les stocker dans une base de données lorsque tous les threads ont terminé leur travail.
Comment puis-je faire cela (être alerté lorsque tous les threads ont terminé leur travail)?
L’approche que j’utilise est d’utiliser un ExecutorService pour gérer des pools de threads.
ExecutorService es = Executors.newCachedThreadPool();
for(int i=0;i<5;i++)
es.execute(new Runnable() { /* your task */ });
es.shutdown();
boolean finished = es.awaitTermination(1, TimeUnit.MINUTES);
// all tasks have finished or the time has been reached.
Vous pouvez join
aux discussions. La jointure est bloquée jusqu'à la fin du thread.
for (Thread thread : threads) {
thread.join();
}
Notez que join
jette une InterruptedException
. Vous devrez décider quoi faire si cela se produit (par exemple, essayez d'annuler les autres threads pour éviter tout travail inutile).
Regardez différentes solutions.
L'API join()
a été introduite dans les premières versions de Java. Depuis la version 1.5 de JDK, ce package concurrent présente de bonnes alternatives.
ExecutorService # invokeAll ()
Exécute les tâches données en renvoyant une liste des contrats à terme contenant leur statut et leurs résultats lorsque tout est terminé.
Reportez-vous à cette question SE associée pour un exemple de code:
Comment utiliser invokeAll () pour laisser tout le pool de threads faire sa tâche?
Une aide à la synchronisation qui permet à un ou plusieurs threads d'attendre la fin d'un ensemble d'opérations effectuées dans d'autres threads.
Un CountDownLatch est initialisé avec un nombre donné. Les méthodes en attente bloquent jusqu'à ce que le nombre actuel atteigne zéro en raison d'appels de la méthode
countDown()
, après quoi tous les threads en attente sont libérés et tous les appels ultérieurs de wait retournent immédiatement. C'est un phénomène ponctuel - le compte ne peut pas être réinitialisé. Si vous avez besoin d’une version qui réinitialise le nombre, envisagez l’utilisation de CyclicBarrier.
Reportez-vous à cette question pour l'utilisation de CountDownLatch
Itérer dans tout Futur objets créés après avoir été soumis à ExecutorService
En plus de Thread.join()
suggéré par d’autres, Java 5 a introduit le framework d’exécuteur. Là, vous ne travaillez pas avec des objets Thread
. Au lieu de cela, vous soumettez vos objets Callable
ou Runnable
à un exécuteur. Il existe un exécuteur spécial conçu pour exécuter plusieurs tâches et renvoyer leurs résultats dans le désordre. C'est la ExecutorCompletionService
:
ExecutorCompletionService executor;
for (..) {
executor.submit(Executors.callable(yourRunnable));
}
Ensuite, vous pouvez appeler plusieurs fois take()
jusqu'à ce qu'il ne reste plus d'objets Future<?>
à renvoyer, ce qui signifie qu'ils sont tous terminés.
Une autre chose qui peut être pertinente, selon votre scénario, est CyclicBarrier
.
Une aide à la synchronisation qui permet à un ensemble de threads de s’attendre mutuellement pour atteindre un point de barrière commun. CyclicBarriers sont utiles dans les programmes impliquant un groupe de threads de taille fixe qui doivent parfois s'attendre les uns les autres. La barrière est appelée cyclique car elle peut être réutilisée une fois les threads en attente libérés.
Une autre possibilité est l’objet CountDownLatch
, qui est utile dans les situations simples: puisque vous connaissez à l’avance le nombre de threads, vous l’initialisez avec le nombre approprié et transmettez la référence de l’objet à chaque thread.
À la fin de sa tâche, chaque thread appelle CountDownLatch.countDown()
qui décrémente le compteur interne. Le thread principal, après avoir démarré tous les autres, doit faire l'appel de blocage CountDownLatch.await()
. Il sera libéré dès que le compteur interne aura atteint 0.
Faites attention avec cet objet, un InterruptedException
peut également être lancé.
Tu fais
for (Thread t : new Thread[] { th1, th2, th3, th4, th5 })
t.join()
Après cette boucle, vous pouvez être sûr que tous les threads ont terminé leur travail.
Stockez les objets Thread dans une collection (telle qu'une liste ou un ensemble), puis parcourez la collection une fois les threads démarrés et appelez join () sur les threads.
Bien que cela ne soit pas pertinent pour OP, si vous êtes intéressé par la synchronisation (plus précisément, un rendez-vous) avec exactement un fil, vous pouvez utiliser un Echangeur
Dans mon cas, je devais mettre le thread parent en pause jusqu'à ce que le thread enfant fasse quelque chose, par exemple. terminé son initialisation. Un CountDownLatch fonctionne aussi bien.
Vous pouvez utiliser Thread # join method à cette fin.
J'ai eu un problème similaire et fini par utiliser Java 8 parallelStream.
requestList.parallelStream().forEach(req -> makeRequest(req));
C’est super simple et lisible . En coulisse, il utilise le pool de jointure de fourches par défaut de la machine virtuelle Java, ce qui signifie qu’il attendra que tous les threads soient terminés avant de continuer. Pour mon cas, c’était une solution intéressante, car c’était le seul programme parallelStream de mon application. Si vous avez plusieurs parallélistes exécutés simultanément, veuillez lire le lien ci-dessous.
Plus d'informations sur les flux parallèles ici .
essayez ceci, fonctionnera.
Thread[] threads = new Thread[10];
List<Thread> allThreads = new ArrayList<Thread>();
for(Thread thread : threads){
if(null != thread){
if(thread.isAlive()){
allThreads.add(thread);
}
}
}
while(!allThreads.isEmpty()){
Iterator<Thread> ite = allThreads.iterator();
while(ite.hasNext()){
Thread thread = ite.next();
if(!thread.isAlive()){
ite.remove();
}
}
}
Attendez/bloquez le fil principal jusqu'à ce que d'autres threads terminent leur travail.
Comme @Ravindra babu
l'a dit, cela peut être réalisé de différentes manières, mais en montrant des exemples.
Java.lang.Thread . join () Depuis: 1.0
public static void joiningThreads() throws InterruptedException {
Thread t1 = new Thread( new LatchTask(1, null), "T1" );
Thread t2 = new Thread( new LatchTask(7, null), "T2" );
Thread t3 = new Thread( new LatchTask(5, null), "T3" );
Thread t4 = new Thread( new LatchTask(2, null), "T4" );
// Start all the threads
t1.start();
t2.start();
t3.start();
t4.start();
// Wait till all threads completes
t1.join();
t2.join();
t3.join();
t4.join();
}
Java.util.concurrent.CountDownLatch Depuis: 1.5
.countDown()
"Décrémente le compte du groupe de verrous..await()
"Les méthodes en attente bloquent jusqu'à ce que le compte actuel atteigne zéro.Si vous avez créé latchGroupCount = 4
, alors countDown()
doit être appelé 4 fois pour que le décompte soit égal à 0. Ainsi, await()
libérera les threads bloquants.
public static void latchThreads() throws InterruptedException {
int latchGroupCount = 4;
CountDownLatch latch = new CountDownLatch(latchGroupCount);
Thread t1 = new Thread( new LatchTask(1, latch), "T1" );
Thread t2 = new Thread( new LatchTask(7, latch), "T2" );
Thread t3 = new Thread( new LatchTask(5, latch), "T3" );
Thread t4 = new Thread( new LatchTask(2, latch), "T4" );
t1.start();
t2.start();
t3.start();
t4.start();
//latch.countDown();
latch.await(); // block until latchGroupCount is 0.
}
Exemple de code de la classe Threaded LatchTask
. Pour tester l'approche, utilisez joiningThreads();
et latchThreads();
de la méthode principale.
class LatchTask extends Thread {
CountDownLatch latch;
int iterations = 10;
public LatchTask(int iterations, CountDownLatch latch) {
this.iterations = iterations;
this.latch = latch;
}
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println(threadName + " : Started Task...");
for (int i = 0; i < iterations; i++) {
System.out.println(threadName + " : " + i);
MainThread_Wait_TillWorkerThreadsComplete.sleep(1);
}
System.out.println(threadName + " : Completed Task");
// countDown() « Decrements the count of the latch group.
if(latch != null)
latch.countDown();
}
}
CyclicBarrier barrier = new CyclicBarrier(3);
barrier.await();
Par exemple, référez-vous à cette classe Concurrent_ParallelNotifyies .Cadre d'exécution: nous pouvons utiliser ExecutorService pour créer un pool de threads et suivre la progression des tâches asynchrones avec Future.
submit(Runnable)
, submit(Callable)
qui renvoient un objet futur. En utilisant la fonction future.get()
, nous pouvons bloquer le thread principal jusqu'à ce que le thread de travail termine son travail.
invokeAll(...)
- renvoie une liste d'objets Future via lesquels vous pouvez obtenir les résultats des exécutions de chaque Callable.
Trouver un exemple d'utilisation du framework Interfaces Runnable, Callable with Executor.
@Voir également
Un service exécuteur peut être utilisé pour gérer plusieurs threads, y compris l'état et l'achèvement. Voir http://programmingexamples.wikidot.com/executorservice
J'ai créé une petite méthode d'assistance pour attendre quelques threads:
public static void waitForThreadsToFinish(Thread... threads) {
try {
for (Thread thread : threads) {
thread.join();
}
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
Les réponses existantes dites pourraient join()
chaque thread.
Mais il existe plusieurs façons d’obtenir le tableau/la liste des threads:
ThreadGroup
pour gérer les threads.Le code suivant utilisera l'approche ThreadGruop
. Il crée d’abord un groupe, puis, lorsque chaque thread est créé, spécifie le groupe dans le constructeur, il peut ultérieurement obtenir le tableau de threads via ThreadGroup.enumerate()
.
SyncBlockLearn.Java
import org.testng.Assert;
import org.testng.annotations.Test;
/**
* synchronized block - learn,
*
* @author eric
* @date Apr 20, 2015 1:37:11 PM
*/
public class SyncBlockLearn {
private static final int TD_COUNT = 5; // thread count
private static final int ROUND_PER_THREAD = 100; // round for each thread,
private static final long INC_DELAY = 10; // delay of each increase,
// sync block test,
@Test
public void syncBlockTest() throws InterruptedException {
Counter ct = new Counter();
ThreadGroup tg = new ThreadGroup("runner");
for (int i = 0; i < TD_COUNT; i++) {
new Thread(tg, ct, "t-" + i).start();
}
Thread[] tArr = new Thread[TD_COUNT];
tg.enumerate(tArr); // get threads,
// wait all runner to finish,
for (Thread t : tArr) {
t.join();
}
System.out.printf("\nfinal count: %d\n", ct.getCount());
Assert.assertEquals(ct.getCount(), TD_COUNT * ROUND_PER_THREAD);
}
static class Counter implements Runnable {
private final Object lkOn = new Object(); // the object to lock on,
private int count = 0;
@Override
public void run() {
System.out.printf("[%s] begin\n", Thread.currentThread().getName());
for (int i = 0; i < ROUND_PER_THREAD; i++) {
synchronized (lkOn) {
System.out.printf("[%s] [%d] inc to: %d\n", Thread.currentThread().getName(), i, ++count);
}
try {
Thread.sleep(INC_DELAY); // wait a while,
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.printf("[%s] end\n", Thread.currentThread().getName());
}
public int getCount() {
return count;
}
}
}
Le thread principal attendra que tous les threads du groupe soient terminés.