Quel est le moyen d'attendre simplement que tous les processus threadés soient terminés? Par exemple, disons que j'ai:
public class DoSomethingInAThread implements Runnable{
public static void main(String[] args) {
for (int n=0; n<1000; n++) {
Thread t = new Thread(new DoSomethingInAThread());
t.start();
}
// wait for all threads' run() methods to complete before continuing
}
public void run() {
// do something here
}
}
Comment puis-je modifier cela afin que la méthode main()
s'interrompt sur le commentaire jusqu'à ce que les méthodes run()
de tous les threads se terminent? Merci!
Vous mettez tous les threads dans un tableau, vous les démarrez tous, puis vous avez une boucle
for(i = 0; i < threads.length; i++)
threads[i].join();
Chaque jointure sera bloquée jusqu'à ce que le thread respectif soit terminé. Les threads peuvent se terminer dans un ordre différent de celui auquel vous les avez joints, mais ce n'est pas un problème: lorsque la boucle se termine, tous les threads sont terminés.
Une solution consisterait à créer un List
sur Thread
s, à créer et à lancer chaque thread tout en l'ajoutant à la liste. Une fois que tout est lancé, parcourez la liste et appelez join()
sur chacun d'entre eux. Peu importe l’ordre dans lequel les threads ont fini de s’exécuter, tout ce que vous devez savoir, c’est que tous les threads seront terminés à la fin de l’exécution de la seconde boucle.
Une meilleure approche consiste à utiliser ExecutorService et ses méthodes associées:
List<Callable> callables = ... // assemble list of Callables here
// Like Runnable but can return a value
ExecutorService execSvc = Executors.newCachedThreadPool();
List<Future<?>> results = execSvc.invokeAll(callables);
// Note: You may not care about the return values, in which case don't
// bother saving them
L'utilisation d'un ExecutorService (et de tous les nouveaux éléments de Java 5 tilitaires de simultanéité =)) est incroyablement flexible et l'exemple ci-dessus à peine égratigne la surface.
import Java.util.ArrayList;
import Java.util.List;
import Java.util.concurrent.ExecutionException;
import Java.util.concurrent.ExecutorService;
import Java.util.concurrent.Executors;
import Java.util.concurrent.Future;
public class DoSomethingInAThread implements Runnable
{
public static void main(String[] args) throws ExecutionException, InterruptedException
{
//limit the number of actual threads
int poolSize = 10;
ExecutorService service = Executors.newFixedThreadPool(poolSize);
List<Future<Runnable>> futures = new ArrayList<Future<Runnable>>();
for (int n = 0; n < 1000; n++)
{
Future f = service.submit(new DoSomethingInAThread());
futures.add(f);
}
// wait for all tasks to complete before continuing
for (Future<Runnable> f : futures)
{
f.get();
}
//shut down the executor service so that this thread can exit
service.shutdownNow();
}
public void run()
{
// do something here
}
}
Évitez la classe Thread et utilisez plutôt les abstractions les plus hautes fournies dans Java.util.concurrent
La classe ExecutorService fournit la méthode invokeAll qui semble faire exactement ce que vous voulez.
au lieu de join()
, qui est une ancienne API, vous pouvez utiliser CountDownLatch . J'ai modifié votre code comme ci-dessous pour répondre à vos besoins.
import Java.util.concurrent.*;
class DoSomethingInAThread implements Runnable{
CountDownLatch latch;
public DoSomethingInAThread(CountDownLatch latch){
this.latch = latch;
}
public void run() {
try{
System.out.println("Do some thing");
latch.countDown();
}catch(Exception err){
err.printStackTrace();
}
}
}
public class CountDownLatchDemo {
public static void main(String[] args) {
try{
CountDownLatch latch = new CountDownLatch(1000);
for (int n=0; n<1000; n++) {
Thread t = new Thread(new DoSomethingInAThread(latch));
t.start();
}
latch.await();
System.out.println("In Main thread after completion of 1000 threads");
}catch(Exception err){
err.printStackTrace();
}
}
}
Explication :
CountDownLatch
a été initialisé avec le nombre donné 1000 selon vos besoins.
Chaque thread de travail DoSomethingInAThread
décrémentera le CountDownLatch
, qui a été passé dans le constructeur.
Fil principal CountDownLatchDemo
await()
jusqu'à ce que le compte soit devenu zéro. Une fois que le compte est devenu zéro, vous obtiendrez une sortie inférieure à la ligne.
In Main thread after completion of 1000 threads
Plus d'infos sur la page de documentation Oracle
public void await()
throws InterruptedException
Amène le thread en cours à attendre que le verrou ait été compté à zéro, à moins que le thread ne soit interrompu.
Reportez-vous à la question SE associée pour d'autres options:
attendez que tous les threads terminent leur travail en Java
Comme Martin K l'a suggéré Java.util.concurrent.CountDownLatch
semble être une meilleure solution pour cela. Ajoutant juste un exemple pour le même
public class CountDownLatchDemo
{
public static void main (String[] args)
{
int noOfThreads = 5;
// Declare the count down latch based on the number of threads you need
// to wait on
final CountDownLatch executionCompleted = new CountDownLatch(noOfThreads);
for (int i = 0; i < noOfThreads; i++)
{
new Thread()
{
@Override
public void run ()
{
System.out.println("I am executed by :" + Thread.currentThread().getName());
try
{
// Dummy sleep
Thread.sleep(3000);
// One thread has completed its job
executionCompleted.countDown();
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}.start();
}
try
{
// Wait till the count down latch opens.In the given case till five
// times countDown method is invoked
executionCompleted.await();
System.out.println("All over");
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
Pensez à utiliser Java.util.concurrent.CountDownLatch
. Exemples dans javadocs
Selon vos besoins, vous pouvez également consulter les classes CountDownLatch et CyclicBarrier dans le package Java.util.concurrent. Ils peuvent être utiles si vous voulez que vos threads s’attendent les uns les autres ou si vous souhaitez un contrôle plus fin sur la façon dont vos threads s’exécutent (par exemple, en attendant, dans leur exécution interne, un autre thread pour définir un état). Vous pouvez également utiliser un CountDownLatch pour signaler à tous vos threads de démarrer en même temps, au lieu de les démarrer un par un lorsque vous parcourez votre boucle. Les documents de l'API standard ont un exemple de cela, plus l'utilisation d'un autre CountDownLatch pour attendre que tous les threads terminent leur exécution.
Si vous dressez une liste des threads, vous pouvez les parcourir et ajouter .join () à chacun d’eux, et votre boucle se terminera lorsque tous les threads auront terminé. Je n'ai pas essayé cependant.
http://docs.Oracle.com/javase/8/docs/api/Java/lang/Thread.html#join ()
Ce serait un commentaire, mais je ne peux pas encore le faire.
Martin [~ # ~] k [~ # ~] , I suis curieux de savoir comment vous utiliseriez ThreadGroup
. L'avez-vous déjà fait?
Je vois que ci-dessus, vous suggérez de vérifier activeCount
- en mettant de côté Martin v Le souci de Löwis à propos du vote de côté, pour le moment, j'ai un autre problème avec activeCount
lui-même.
Mise en garde: Je n’ai pas essayé d’utiliser ceci, donc je ne suis pas un expert en la matière, mais selon le javadocs , il retourne un estimation du nombre de threads actifs.
Personnellement, je ne voudrais pas essayer de construire un système sur une estimation. Avez-vous une autre idée sur la façon de le faire ou est-ce que je me trompe dans le javadoc?
Créez l'objet thread dans la première boucle for.
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new Runnable() {
public void run() {
// some code to run in parallel
}
});
threads[i].start();
}
Et alors, voilà ce que tout le monde dit ici.
for(i = 0; i < threads.length; i++)
threads[i].join();
Au lieu de CountDownLatch, vous pouvez également utiliser CyclicBarrier p. Ex.
public class ThreadWaitEx {
static CyclicBarrier barrier = new CyclicBarrier(100, new Runnable(){
public void run(){
System.out.println("clean up job after all tasks are done.");
}
});
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
Thread t = new Thread(new MyCallable(barrier));
t.start();
}
}
}
class MyCallable implements Runnable{
private CyclicBarrier b = null;
public MyCallable(CyclicBarrier b){
this.b = b;
}
@Override
public void run(){
try {
//do something
System.out.println(Thread.currentThread().getName()+" is waiting for barrier after completing his job.");
b.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
Pour utiliser CyclicBarrier dans ce cas, barrier.await () doit être la dernière instruction, c’est-à-dire lorsque votre thread a terminé son travail. CyclicBarrier peut être utilisé à nouveau avec sa méthode reset (). Pour citer des javadocs:
Un CyclicBarrier prend en charge une commande optionnelle Runnable qui est exécutée une fois par barrière, après l'arrivée du dernier thread de la partie, mais avant la libération des threads. Cette action de barrière est utile pour mettre à jour l'état partagé avant que l'une des parties ne continue.
Vous pouvez le faire avec l’objet "ThreadGroup" et son paramètre activeCount :
La join()
ne m'a pas été utile. voir cet exemple à Kotlin:
val timeInMillis = System.currentTimeMillis()
ThreadUtils.startNewThread(Runnable {
for (i in 1..5) {
val t = Thread(Runnable {
Thread.sleep(50)
var a = i
kotlin.io.println(Thread.currentThread().name + "|" + "a=$a")
Thread.sleep(200)
for (j in 1..5) {
a *= j
Thread.sleep(100)
kotlin.io.println(Thread.currentThread().name + "|" + "$a*$j=$a")
}
kotlin.io.println(Thread.currentThread().name + "|TaskDurationInMillis = " + (System.currentTimeMillis() - timeInMillis))
})
t.start()
}
})
Le résultat:
Thread-5|a=5
Thread-1|a=1
Thread-3|a=3
Thread-2|a=2
Thread-4|a=4
Thread-2|2*1=2
Thread-3|3*1=3
Thread-1|1*1=1
Thread-5|5*1=5
Thread-4|4*1=4
Thread-1|2*2=2
Thread-5|10*2=10
Thread-3|6*2=6
Thread-4|8*2=8
Thread-2|4*2=4
Thread-3|18*3=18
Thread-1|6*3=6
Thread-5|30*3=30
Thread-2|12*3=12
Thread-4|24*3=24
Thread-4|96*4=96
Thread-2|48*4=48
Thread-5|120*4=120
Thread-1|24*4=24
Thread-3|72*4=72
Thread-5|600*5=600
Thread-4|480*5=480
Thread-3|360*5=360
Thread-1|120*5=120
Thread-2|240*5=240
Thread-1|TaskDurationInMillis = 765
Thread-3|TaskDurationInMillis = 765
Thread-4|TaskDurationInMillis = 765
Thread-5|TaskDurationInMillis = 765
Thread-2|TaskDurationInMillis = 765
Maintenant, laissez-moi utiliser la join()
pour les discussions:
val timeInMillis = System.currentTimeMillis()
ThreadUtils.startNewThread(Runnable {
for (i in 1..5) {
val t = Thread(Runnable {
Thread.sleep(50)
var a = i
kotlin.io.println(Thread.currentThread().name + "|" + "a=$a")
Thread.sleep(200)
for (j in 1..5) {
a *= j
Thread.sleep(100)
kotlin.io.println(Thread.currentThread().name + "|" + "$a*$j=$a")
}
kotlin.io.println(Thread.currentThread().name + "|TaskDurationInMillis = " + (System.currentTimeMillis() - timeInMillis))
})
t.start()
t.join()
}
})
Et le résultat:
Thread-1|a=1
Thread-1|1*1=1
Thread-1|2*2=2
Thread-1|6*3=6
Thread-1|24*4=24
Thread-1|120*5=120
Thread-1|TaskDurationInMillis = 815
Thread-2|a=2
Thread-2|2*1=2
Thread-2|4*2=4
Thread-2|12*3=12
Thread-2|48*4=48
Thread-2|240*5=240
Thread-2|TaskDurationInMillis = 1568
Thread-3|a=3
Thread-3|3*1=3
Thread-3|6*2=6
Thread-3|18*3=18
Thread-3|72*4=72
Thread-3|360*5=360
Thread-3|TaskDurationInMillis = 2323
Thread-4|a=4
Thread-4|4*1=4
Thread-4|8*2=8
Thread-4|24*3=24
Thread-4|96*4=96
Thread-4|480*5=480
Thread-4|TaskDurationInMillis = 3078
Thread-5|a=5
Thread-5|5*1=5
Thread-5|10*2=10
Thread-5|30*3=30
Thread-5|120*4=120
Thread-5|600*5=600
Thread-5|TaskDurationInMillis = 3833
Comme il est clair lorsque nous utilisons le join
:
Notre solution pour empêcher le blocage d'autres threads était de créer un ArrayList:
val threads = ArrayList<Thread>()
Maintenant, lorsque nous voulons démarrer un nouveau fil, nous l'ajoutons le plus à ArrayList:
addThreadToArray(
ThreadUtils.startNewThread(Runnable {
...
})
)
La fonction addThreadToArray
:
@Synchronized
fun addThreadToArray(th: Thread) {
threads.add(th)
}
Le startNewThread
funstion:
fun startNewThread(runnable: Runnable) : Thread {
val th = Thread(runnable)
th.isDaemon = false
th.priority = Thread.MAX_PRIORITY
th.start()
return th
}
Vérifiez l'achèvement des discussions comme ci-dessous partout où c'est nécessaire:
val notAliveThreads = ArrayList<Thread>()
for (t in threads)
if (!t.isAlive)
notAliveThreads.add(t)
threads.removeAll(notAliveThreads)
if (threads.size == 0){
// The size is 0 -> there is no alive threads.
}