1.   
    1. Math.random() renvoie un flottant pseudo-aléatoire (en double précision, un double) entre 0.0 et 1.0 (exclu). La multiplication et la conversion de type donnent finalement un entier pseudo-aléatoire entre 0 et 3 (exclu).

    2. Le programme est normalement lancé comme java Lion nn est un entier. On peut omettre l'argument n, auquel cas une valeur par défaut est substitué à n.

      L'interface n'est pas parfaite. Il faudrait :
      • Se garder des arguments non-entiers ! (Exception NumberFormatException).
      • Exiger 0 ou 1 argument exactement.
      • Signaler les argument absurdes (négatifs).
      Dans tous ces cas, une solution courante est de faire échouer le programme et d'afficher un message résumant l'interface utlisateur du programme.

    3. La construction en question peut se lire comme (new Lion()).run(). Elle crée un objet de la classe Lion dont la méthode run est immédiatement appelée.


  2. Il n'y a en fait qu'un seul thread, car run est invoquée directement.

  3. À la différence de run, la méthode start n'exécute pas run elle délègue cette tâche à un nouveau thread crée pour l'occasion, et retourne tout de suite. Autrement dit l'identification des threads aux objets de la classe Thread a ses limites.

    Un thread est stricto-sensu une instance (de bout de) programme en cours d'exécution, et il n'est pas facile de réduire cette notion étrange à un objet.

    Bref, appelons l0 et l1 nos deux threads lions, nous disposons aussi d'un thread principal (celui qui exécute main) chargé à la fois de l'éveil des lions et de la lecture du dispositif. Voici les trois scénarios :
    1. p s'exécute complètement avant l0 et l1, la valeur affichée est zéro.

    2. p lance l0, et donne immédiatement la main à l0 qui s'exécute complètement, puis reprend la main, l1 ne s'exécute qu'une foisp terminé. La valeur affichée est 1.

    3. p passe la main au lions, dès que celui-ci est lancé, puis reprend la main dès que le lion est couché. La valeur affichée est 2.


  4. Évidemment on ne veut relever le dispositif qu'une fois tous les lions couchés. Voici une première solution qui échoue si le zoologue est interrompu :
     public static void main(String [] arg) {
        
    int n = 64 ;
        
    if (arg.length > 0) n = Integer.parseInt(arg[0]) ;
        
    Thread [] t = new Thread [n] ;

        
    for (int i = 0 ; i < n ; i++) {
          t[i] = 
    new Lion() ;
          t[i].start() ;
        }

        
    try {
          
    for (int i = 0 ; i < n ; i++) {
            t[i].join() ;
          }
        } 
    catch (InterruptedException e) { System.exit(2) ; }
        System.out.println("Bilan: " + mare) ;
      }

    Pour ignorer les interruptions, il faut recomencer à attendre le coucher du lion surveillé au moment de l'interruption. La boucle finale devient :
        for (int i = 0 ; i < n ; i++) {
          
    for ( ; ; ) {
            
    try { t[i].join() ; break ; }
            
    catch (InterruptedException e) { }
          }
        }
    On note la boucle infinie, dont on sort par break.

  5. Le défaut peut se résumer simplement : le compteur peut compter plusieurs lions se présentant simultanément comme un seul.

    De fait l'instruction c++ n'est pas atomique, elle peut se décomposer ainsi : d'abord, lecture de la variable partagée c dans une case propre à chaque thread-lion (attention c'est bien le lion qui boit, c'est son thread qui exécute le code de la méthode boire) ; puis incrément; et enfin écriture de la nouvelle valeur dans la variable partagée c.

    On peut donc imaginer le scénario catastrophe suivant : tous les lions se présentent en même temps à la mare, et donc lisent tous la même valeur de c. Une fois les écritures complétées tout se passe, au vu du compteur, comme si un seul lion était passé. En terme d'ordonancement sur un seul processeur, ce scénario survient quand les lions passent systématiquement la main après la lecture de c.

    Toutefois, du point de vue d'un lion donné, tous ses passages sont enregistrés, puisque les lectures/écritures du compteur par un lion donné s'effectuent séquentiellement. Ainsi, la valeur finale du compteur vaut au minimun le nombre de passages à la mare d'un lion arbitraire et au maximum la somme des passages à la mare de tous les lions.

  6. En fonction de la discussion précédente.
    1. 2 passages enregistrés.
    2. Entre 2 et 4 passages enregistrés.
    3. Entre 1 et 3 passages enregistrés.


  7. On remédie au défaut en déclarant la méthode boire comme synchronized, ainsi un seul lion peut accéder au compteur à la fois. L'effet de synchronized est de faire qu'au plus un thread peut se trouver dans le code de boire (notez que ce thread peut être prêt [voir le poly] c'est à dire ne s'exécutant pas). On dit aussi que le code de boire est en section critique.