Comment démarrer deux threads «exactement» en même temps

91

Les threads devraient commencer à la même fraction de seconde. Je comprends que si vous le faites thread1.start(), il faudra quelques millisecondes avant la prochaine exécution de thread2.start().

Est-ce même possible ou impossible?

Figaro
la source
2
La fraction de seconde est un temps très long à des vitesses GHz.
Nikolai Fetissov
30
Pas besoin de voter si intensément. Tout le monde ne comprend pas le non-déterminisme lié au threading, et nous devons tous commencer quelque part.
Michael Petrotta
7
Je ne comprends pas les votes négatifs. La synchronisation entre les threads est une nécessité très courante. Oui, en java, vous ne pouvez pas les faire s'exécuter exactement en parallèle (ce qui, sur une autre plate-forme peut être une exigence très valide btw), mais de temps en temps, il est très courant que vous deviez synchroniser leur action. C'est pourquoi le jdk a des classes pour faire cela. Peut-être que le libellé n'était pas exact, mais diable s'il avait su cela, il n'aurait pas posé la question ..
Enno Shioji
eh bien, je suppose que je comprends toute ta colère. C'était une question qui m'a été posée lors d'une interview ... c'était probablement un truc. Q. Mais, je suis juste devenu confus et je voulais le confirmer. c'est pourquoi j'ai demandé si c'était même possible.
figaro
2
@javaguy - pas une question "piège". Plutôt une question choisie pour voir à quel point vous comprenez vraiment la programmation multi-thread en général ... ainsi que dans le cas de Java.
Stephen C

Réponses:

135

Pour démarrer les threads exactement au même moment (au moins aussi bon que possible), vous pouvez utiliser un CyclicBarrier :

// We want to start just 2 threads at the same time, but let's control that 
// timing from the main thread. That's why we have 3 "parties" instead of 2.
final CyclicBarrier gate = new CyclicBarrier(3);

Thread t1 = new Thread(){
    public void run(){
        gate.await();
        //do stuff    
    }};
Thread t2 = new Thread(){
    public void run(){
        gate.await();
        //do stuff    
    }};

t1.start();
t2.start();

// At this point, t1 and t2 are blocking on the gate. 
// Since we gave "3" as the argument, gate is not opened yet.
// Now if we block on the gate from the main thread, it will open
// and all threads will start to do stuff!

gate.await();
System.out.println("all threads started");

Cela ne doit pas nécessairement être un CyclicBarrier, vous pouvez également utiliser un CountDownLatchou même un verrou.

Cela ne permet toujours pas de s'assurer qu'ils sont démarrés exactement au même moment sur les JVM standard, mais vous pouvez vous en approcher assez. Se rapprocher est toujours utile lorsque vous effectuez par exemple des tests de performances. Par exemple, si vous essayez de mesurer le débit d'une structure de données avec un nombre différent de threads qui l'atteignent, vous souhaitez utiliser ce type de construction pour obtenir le résultat le plus précis possible.

Sur d'autres plates-formes, démarrer exactement les threads peut être une exigence très valide btw.

Enno Shioji
la source
5
+1 belle réflexion ;-) Bien que bien sûr, cela ne fasse pas démarrer les threads exactement au même moment en temps réel (du moins pas sur une puce monocœur, et pas garanti même sur une puce multicœur), mais Je ne vois aucun moyen de faire mieux.
David Z
Est-ce que ce raccordement aux poignées d'attente spécifiques à l'environnement?
ChaosPandion
@ChaosPandion: Voulez-vous élaborer?
Santa
@Santa - L'API Win32 par exemple propose différentes primitives. Un type utile est l'événement de réinitialisation manuelle renvoyé lorsque vous appelez CreateEvent. msdn.microsoft.com/en-us/library/ms686364%28VS.85%29.aspx
ChaosPandion
1
@Zwei - Quoi qu'il en soit, c'est la réponse que j'aurais publiée si j'avais été un gourou de Java.
ChaosPandion
15

Ce n'est pas possible, du moins sur un ordinateur à un seul cœur. Mais pourquoi veux-tu ça? Même si vous avez pu démarrer deux threads exactement à la même seconde, ils progresseront différemment car la planification n'est pas sous votre contrôle.

Edit: (En réponse à certains des commentaires) C'est une exigence parfaitement valide pour synchroniser l'état ou la progression de plusieurs threads et CyclicBarrierc'est un excellent outil. J'ai répondu à la question de savoir s'il est possible de démarrer plusieurs threads exactement en même temps . CyclicBarriergarantira que les threads se déroulent quand ils sont exactement à l'état souhaité, mais cela ne garantit pas qu'ils démarreront ou reprendront exactement au même moment, bien que cela puisse être assez proche. Il n'y a aucune mention des besoins de synchronisation dans la question.

samitgaur
la source
1
Vous pouvez cependant vous en rapprocher. Tout dépend des techniques de synchronisation que vous utilisez et bien sûr d'avoir plus d'un processeur ou core.
ChaosPandion
L'idée est que c'est une approche erronée et ne devrait pas être nécessaire dans un environnement en temps réel pas difficile, non pas que ce soit "assez proche".
Nikolai Fetissov
1
C'est également impossible car la planification des threads Java ne vous donne pas une précision à la milliseconde.
Stephen C
1
@Zwei - vous pouvez probablement être "très proche" la plupart du temps sur une machine autrement inactive. Mais si vous en avez besoin tout le temps, vous devez programmer dans un langage comme C, sur une machine avec un support en temps réel dur dans le système d'exploitation. Considérez également que la JVM peut exécuter un GC complet à la «fraction de seconde» à laquelle vous souhaitez démarrer vos threads.
Stephen C
1
C'est un problème de synchronisation parfaitement valable. Les threads doivent initialiser certaines données, assurez-vous que personne n'entre dans une région critique sans une validation appropriée. Et le fait que CyclicBerrier soit inclus dans le package concurrent javas signifie que c'est un problème important.
Denis Tulskiy
14

Vous pouvez utiliser un CountDownLatch pour cela. Veuillez trouver ci-dessous un échantillon. Bien que t1 et t2 soient démarrés, ces threads continuent d'attendre jusqu'à ce que le thread principal décompte le verrou. Le nombre de comptes à rebours requis est mentionné dans le constructeur. Le verrou du compte à rebours peut également être utilisé pour attendre la fin de l'exécution des threads afin que le thread principal puisse continuer (le cas inverse). Cette classe est incluse depuis Java 1.5.

import java.util.concurrent.CountDownLatch;


public class ThreadExample
{
    public static void main(String[] args) 
    {
        CountDownLatch latch = new CountDownLatch(1);
        MyThread t1 = new MyThread(latch);
        MyThread t2 = new MyThread(latch);
        new Thread(t1).start();
        new Thread(t2).start();
        //Do whatever you want
        latch.countDown();          //This will inform all the threads to start
        //Continue to do whatever
    }
}

class MyThread implements Runnable
{
    CountDownLatch latch;
    public MyThread(CountDownLatch latch) 
    {
        this.latch = latch;
    }
    @Override
    public void run() 
    {
        try 
        {
            latch.await();          //The thread keeps waiting till it is informed
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //Do the actual thing
    }
}
aNish
la source
Cette réponse pourrait bénéficier de moins de passe-partout dans l'exemple de code.
user2418306
6
  1. Si je comprends bien, la JVM délègue principalement ces éléments au système d'exploitation. La réponse sera donc spécifique au système d'exploitation.
  2. C'est clairement impossible sur une machine mono-processeur.
  3. C'est plus compliqué par rapport à une machine multiprocesseur. Selon la Relativité de la simultanéité , "il est impossible de dire dans un sens absolu si deux événements se produisent en même temps si ces événements sont séparés dans l'espace". Quelle que soit la proximité de vos processeurs, ils sont séparés dans l'espace.
    1. Si vous pouvez accepter la simultanéité relative, alors il est probablement plus facile de simplement la simuler à l'aide des techniques décrites dans d'autres réponses.
émory
la source
1
... et même si nous supposons un démarrage simultané, chaque thread a sa propre chronologie, tous étant simultanés , mais pas nécessairement parallèles, donc tout ce que quelqu'un suppose sur la simultanéité des threads peut (sera) être nul dans la prochaine nanoseconde (de n'importe quel chronologie)…
Holger