1  Parallélisme

Eric Goubault

Commissariat à l'Energie Atomique

Saclay

2  Plan du cours

3  Etats d'un thread

Un thread peut être dans l'un des 4 cas suivants:

4  Etats d'un thread

5  Etats d'un thread

6  Priorités

7  Processus démons

On peut également déclarer un processus comme étant un démon ou pas:

setDaemon(Boolean on);
boolean isDaemon();
``support'' aux autres (horloge, ramasse-miettes...). Il est détruit quand il n'y a plus aucun processus utilisateur (non-démon) restant

8  Ordonnancement tâches (cas 1 processeur)

9  Ordonnancement de tâches

10  En fait...

L'ordonnancement dépend aussi bien de l'implémentation de la JVM que du système d'exploitation sous-jacent; 2 modèles principaux:

11  Encore des comptes...

public class Compte {
    private int valeur;
    
    Compte(int val) {
        valeur = val;
    }
    
    public int solde() {
        return valeur;
    }

12  

  
    public void depot(int somme) {
        if (somme > 0)
            valeur+=somme;
    }
    
    public boolean retirer(int somme) 
           throws InterruptedException {
        if (somme > 0)
            if (somme <= valeur) {
                Thread.currentThread().sleep(50);
                valeur -= somme;
                Thread.currentThread().sleep(50);
                return true;
            }
        return false;
    } }

13  La banque...

public class Banque implements Runnable {
    Compte nom;
    
    Banque(Compte n) {
         nom = n; }

    public void Liquide (int montant) 
           throws InterruptedException {
         if (nom.retirer(montant)) {
             Thread.currentThread().sleep(50);

14  

            Donne(montant);
            Thread.currentThread().sleep(50); }
        ImprimeRecu();
        Thread.currentThread().sleep(50); }

    public void Donne(int montant) {
        System.out.println(Thread.currentThread().
getName()+": Voici vos " + montant + " francs."); }

    public void ImprimeRecu() {
        if (nom.solde() > 0)
            System.out.println(Thread.currentThread().
getName()+": Il vous reste " + nom.solde() + " francs.");
        else
            System.out.println(Thread.currentThread().
        getName()+": Vous etes fauches!");

15  

    }

    public void run() {
        try {
           for (int i=1;i<10;i++) {
                Liquide(100*i);
                Thread.currentThread().sleep(100+10*i);
           }
        } catch (InterruptedException e) {
            return;
        }   
    }   

    public static void main(String[] args) {
        Compte Commun = new Compte(1000);

16  

        Runnable Mari = new Banque(Commun);
        Runnable Femme = new Banque(Commun);
        Thread tMari = new Thread(Mari);
        Thread tFemme = new Thread(Femme);
        tMari.setName("Conseiller Mari");
        tFemme.setName("Conseiller Femme");
        tMari.start();
        tFemme.start();
    }
}

17  Une exécution

% java Banque
Conseiller Mari: Voici vos 100 francs.
Conseiller Femme: Voici vos 100 francs.
Conseiller Mari: Il vous reste 800 francs.
Conseiller Femme: Il vous reste 800 francs.
Conseiller Mari: Voici vos 200 francs.
Conseiller Femme: Voici vos 200 francs.
Conseiller Femme: Il vous reste 400 francs.
Conseiller Mari: Il vous reste 400 francs.
Conseiller Mari: Voici vos 300 francs.
Conseiller Femme: Voici vos 300 francs.
Conseiller Femme: Vous etes fauches!
Conseiller Mari: Vous etes fauches! ...

18  Résultat...

19  Explication

(``Sémantique'' -- certes idéalisée...)

20  Sémantique par entrelacements


21  Explication

Si les 2 threads tMari et tFemme sont exécutés de telle façon que dans retirer, chaque étape soit faite en même temps:

22  Sémantique

Langage simple, ``fragment de JAVA'':

EXPR ::= CONSTANTE | VARIABLE
  | EXPR+EXPR | EXPR*EXPR
| EXPR/EXPR | EXPR-EXPR
TEST ::= EXPR==EXPR | EXPR < EXPR
| EXPR > EXPR | TEST && TEST
| TEST | | TEST | ! TEST
INSTR ::= VARIABLE=EXPR

23  Sémantique

BLOCK ::= e
  | INSTR;BLOCK
| if TEST
    then BLOCK
    else BLOCK ;BLOCK
  | while TEST
      BLOCK ;
    BLOCK

24  Systèmes de transitions

Sémantique d'un BLOCK inductivement sur la syntaxe, en lui associant ce que l'on appelle un système de transitions: 4-uplet (S,i,E,Tran) où,

25  Sémantique

26  Sémantique

27  Sémantique

28  Entrelacements

Ensemble de processus P1,...,Pn Î L. ``Entrelacements'':

29  Exécutions

30  Une solution

31  Maintenant...

% java Banque
Conseiller Mari: Voici vos 100 francs.
Conseiller Mari: Il vous reste 800 francs.
Conseiller Femme: Voici vos 100 francs.
Conseiller Femme: Il vous reste 800 francs.
Conseiller Mari: Voici vos 200 francs.
Conseiller Mari: Il vous reste 400 francs.
Conseiller Femme: Voici vos 200 francs.
Conseiller Femme: Il vous reste 400 francs.
Conseiller Femme: Il vous reste 100 francs.
Conseiller Mari: Voici vos 300 francs.
Conseiller Mari: Il vous reste 100 francs.
Conseiller Femme: Il vous reste 100 francs.
Conseiller Mari: Il vous reste 100 francs...

32  Résultat...

33  Peut on se passer de synchronized?

public class CS1 extends Thread {
    Thread tour = null;
    
    public void AttendtonTour() {
        while (tour != Thread.currentThread()) {
            if (tour == null)
                tour = Thread.currentThread();
            try {
                Thread.sleep(100);
            } catch (Exception e) {}
        }
    }

34  

    public void RendlaMain() {
        if (tour == Thread.currentThread()) 
            tour = null;
    }

    public void run() {
        while(true) {
            AttendtonTour();
            System.out.println("C'est le tour de "+
                   Thread.currentThread().getName());
            RendlaMain();
        } }

35  

    public static void main(String[] args) {
        Thread Un = new CS1();
        Thread Deux = new CS1();
        Un.setName("UN");
        Deux.setName("DEUX");
        Un.start();
        Deux.start();
    } }
Problème: exécution synchrone des threads!

36  wait() et notify()

Chaque objet fournit un verrou, mais aussi un mécanisme de mise en attente (forme primitive de communication inter-threads; similaire aux variables de conditions ou aux moniteurs):

37  Sémaphores

public class Semaphore {
    int n;
    String name;

    public Semaphore(int max, String S) {
        n = max;
        name = S;
    }

38  

    public synchronized void P() {
        if (n == 0) {
            try {
                 wait();
            } catch(InterruptedException ex) {};
        }
        n--;
        System.out.println("P("+name+")");        
    }

    public synchronized void V() {
        n++;
        System.out.println("V("+name+")");
        notify();
    }
}

39  Section Critique

public class essaiPV extends Thread {
    static int x = 3;
    Semaphore u;

    public essaiPV(Semaphore s) {
        u = s;
    }

    public void run() {
        int y;
        // u.P();

40  

        try {
            Thread.currentThread().sleep(100);
            y = x;
            Thread.currentThread().sleep(100);
            y = y+1;
            Thread.currentThread().sleep(100);
            x = y;
            Thread.currentThread().sleep(100);
        } catch (InterruptedException e) {};
        System.out.println(Thread.currentThread().
                           getName()+": x="+x);
        // u.V(); 
    }

41  

    public static void main(String[] args) {
        Semaphore X = new Semaphore(1,"X");
        new essaiPV(X).start();
        new essaiPV(X).start();
    }
}

42  Résultat (sans P, V)

% java essaiPV
Thread-2: x=4
Thread-3: x=4

43  Résultat (avec P, V)

% java essaiPV
P(X)
Thread-2: x=4
V(X)
P(X)
Thread-3: x=5
V(X)

44  Un peu de sémantique

On rajouter à L: P(VARIABLE) et V(VARIABLE). La sémantique par systèmes de transitions change assez peu:

45  Sémantique

L'apparition de ces nouvelles transitions contraint les entrelacements possibles.

46  Une ``abstraction''

L'idée de base: ``Progress graphs'' (E.W.Dijkstra (1968))

T1=Pa.Pb.Vb.Va en parallèle avec T2=Pb.Pa.Va.Vb


``Image continue'': xi = temps local

Traces = chemins croissants dans chaque coordonnée = ``di-chemins''

47  Portée de synchronized

48  Pour être plus précis...

49  Pour être plus précis

51  En fait...

Actions de la mémoire principale (la JVM): Pour les types ``plus longs'' comme double et long, la spécification permet tout à fait que load, store, read et write soient non-atomiques.

52  Contraintes

Pour un thread donné:

53  Contraintes

Pour une variable donnée et un thread donné,

54  Exemples

class Essai1 extends Thread {
  public Essai1(Exemple1 e) {
    E = e;  }

  public void run() {
    E.F1();  }

  private Exemple1 E;  } 

55  Exemples

class Essai2 extends Thread {
  public Essai2(Exemple1 e) {
    E = e;  }

  public void run() {
    E.G1();  }

  private Exemple1 E;  } 

56  Exemples

class Lance {
  public static void main(String args[]) {
    Exemple1 e = new Exemple1();
    
    Essai1 T1 = new Essai1(e);
    Essai2 T2 = new Essai2(e);

    T1.start();
    T2.start();
    System.out.println("From Lance a="+e.a+" b="+e.b);  } } 

57  Exemples

class Exemple1 {
  int a = 1;
  int b = 2;

  void F1() {
    a = b;  }

  void G1() {
    b = a;  }  }

58  Exemples

class Exemple2 {
  int a = 1;
  int b = 2;

  synchronized void F2() {
    a = b;  }

  synchronized void G2() {
    b = a;  } }

59  Exécutions

> java Lance
a=2 b=2
> java Lance
a=1 b=1
> java Lance
a=2 b=1

60  Explication

Les chemins possibles d'exécution sont les entrelacements des 2 traces séquentielles:

read b ® load b ® use b ® assign a ® store a ® write a

pour le thread Essai1 et

read a ® load a ® use a ® assign b ® store b ® write b

pour le thread Essai2.

61  Explication

Appelons a1, b1 (respectivement a2 et b2) les copies locales des variables a et b pour le thread Essai1 (respectivement pour le thread Essai2):

write a ® read a ® read b ® write b
read a ® write a ® write b ® read b
read a ® write a ® read b ® write b

62  Explication

Si on utilise la version synchronisée de Exemple1: Exemple2, alors on n'a plus que les deux premiers ordonnancements qui soient possibles.

63  Philosophes qui dînent


64  Philosophes qui dînent

public class Phil extends Thread {
    Semaphore LeftFork;
    Semaphore RightFork;

    public Phil(Semaphore l, Semaphore r) {
        LeftFork = l;
        RightFork = r;
    }

65  

    public void run() {
        try {
            Thread.currentThread().sleep(100);
            LeftFork.P();
            Thread.currentThread().sleep(100);
            RightFork.P();
            Thread.currentThread().sleep(100);
            LeftFork.V();
            Thread.currentThread().sleep(100);
            RightFork.V();
            Thread.currentThread().sleep(100);
        } catch (InterruptedException e) {};
    }
}

66  

public class Dining {

    public static void main(String[] args) {
        Semaphore a = new Semaphore(1,"a");
        Semaphore b = new Semaphore(1,"b");
        Semaphore c = new Semaphore(1,"c");
        Phil Phil1 = new Phil(a,b);
        Phil Phil2 = new Phil(b,c);
        Phil Phil3 = new Phil(c,a);
        Phil1.setName("Kant");
        Phil2.setName("Heidegger");
        Phil3.setName("Spinoza");
        Phil1.start();
        Phil2.start();
        Phil3.start();
    } }

67  Résultat

% java Dining
Kant: P(a)
Heidegger: P(b)
Spinoza: P(c)
^C

68  Interblocage

69  Explication

/* 3 philosophers ``3p'' */
A=Pa.Pb.Va.Vb
B=Pb.Pc.Vb.Vc
C=Pc.Pa.Vc.Va





70  Une solution possible

71  Une solution

P1=Pa2.Pa1.Va2.Va1
P2=Pa2.Pa3.Va2.Va3
...
P2k=Pa2k.Pa(2k+1).Va2k.Va(2k+1)
P(2k+1)=Pa(2k+2).Pa(2k+1).Va(2k+2).Va(2k+1)

72  Sémaphores à compteur

public class essaiPVsup extends Thread {
    static int x = 3;
    Semaphore u;

    public essaiPVsup(Semaphore s) {
        u = s;
    }

73  

    public void run() {
        int y;
        u.P();
        try {
            Thread.currentThread().sleep(100);
            y = x;
            Thread.currentThread().sleep(100);
            y = y+1;
            Thread.currentThread().sleep(100);
            x = y;
            Thread.currentThread().sleep(100);
        } catch (InterruptedException e) {};
        System.out.println(Thread.currentThread().
                           getName()+": x="+x);
        u.V(); }

74  

    public static void main(String[] args) {
        Semaphore X = new Semaphore(2,"X");
        new essaiPVsup(X).start();
        new essaiPVsup(X).start();
    }
}

75  Résultat

% java essaiPVsup
Thread-2: P(X)
Thread-3: P(X)
Thread-2: x=4
Thread-2: V(X)
Thread-3: x=4
Thread-3: V(X)
Pas de protection!

76  Réel intérêt

Pour les tampons de capacité bornée (problème du type producteur consommateur):

public class essaiPVsup2 extends Thread {
    static String[] x = {null,null};
    Semaphore u;

    public essaiPVsup2(Semaphore s) {
        u = s;
    }

77  

    public void push(String s) {
        x[1] = x[0];
        x[0] = s;
    }

    public String pop() {
        String s = x[0];
        x[0] = x[1];
        x[1] = null;
        return s; }

    public void produce() {
        push(Thread.currentThread().getName());
        System.out.println(Thread.currentThread().
                           getName()+": push");
    }

78  

    public void consume() {
        pop();
        System.out.println(Thread.currentThread().
                           getName()+": pop"); }

    public void run() {
        try {
            u.P();
            produce();
            Thread.currentThread().sleep(100);
            consume();
            Thread.currentThread().sleep(100);
            u.V();
        } catch(InterruptedException e) {};
    }

79  

    public static void main(String[] args) {
        Semaphore X = new Semaphore(2,"X");
        new essaiPVsup2(X).start();
        new essaiPVsup2(X).start();
        new essaiPVsup2(X).start();
    }
}

80  Résultat

% java essaiPVsup2
Thread-2: P(X)
Thread-2: push
Thread-3: P(X)
Thread-3: push
Thread-2: pop
Thread-3: pop
Thread-2: V(X)
Thread-3: V(X)
Thread-4: P(X)
Thread-4: push
Thread-4: pop
Thread-4: V(X)

81  Sérialisation

public class Ticket {
    String nom;}
public class Requete extends Thread {
    Ticket Aller;
    Ticket Retour;
    Semaphore u;
    Semaphore v;
    public Requete(Ticket t, Ticket s, 
                   Semaphore x, Semaphore y) {
        Aller = t;
        Retour = s;
        u = x;
        v = y; }

82  

    public void run() {
        try {
            u.P();
            Thread.currentThread().sleep(100);
            if (Aller.nom == null)
                 Aller.nom = Thread.currentThread().
                                    getName();
            Thread.currentThread().sleep(100);
            u.V();
        } catch (InterruptedException e) {};

83  

        try {
            v.P();
            Thread.currentThread().sleep(100);
            if (Retour.nom == null)
                Retour.nom = Thread.currentThread().
                                    getName();
            Thread.currentThread().sleep(100);
            v.V();
        } catch (InterruptedException e) {};
    }
}

84  

public class Serial {
    public static void main(String[] args) {
        Semaphore a = new Semaphore(1,"a");
        Semaphore b = new Semaphore(1,"b");
        Ticket s = new Ticket();
        Ticket t = new Ticket();
        Requete Bill = new Requete(s,t,a,b);
        Requete Tom = new Requete(t,s,b,a);
        Bill.setName("Bill");
        Tom.setName("Tom");
        Bill.start();
        Tom.start();
        try {
            Tom.join();
            Bill.join();
        } catch(InterruptedException e) {};

85  

        System.out.println("Le billet Paris->New York 
                            est au nom de: "+s.nom);
        System.out.println("Le billet New York->Paris 
                            est au nom de: "+t.nom); } }

86  Résultat...

% java Serial
Bill: P(a)
Tom: P(b)
Bill: V(a)
Tom: V(b)
Bill: P(b)
Tom: P(a)
Bill: V(b)
Tom: V(a)
Le billet Paris->New York est au nom de: Bill
Le billet New York->Paris est au nom de: Tom

87  Progress graph


88  2-phase locking

Changer dans Requete, l'ordre des verrous, dans la méthode run():

    public void run() {
        try {
            u.P();
            v.P();
            Thread.currentThread().sleep(100);
            if (Aller.nom == null)
                Aller.nom = Thread.currentThread().
                                   getName();
            Thread.currentThread().sleep(100);

89  

            Thread.currentThread().sleep(100);
            if (Retour.nom == null)
                Retour.nom = Thread.currentThread().
                                    getName();
            Thread.currentThread().sleep(100);
            u.V();
            v.V();
        } catch (InterruptedException e) {};
    }

90  Résultat

% java Serial2
Bill: P(a)
Bill: P(b)
Bill: V(a)
Bill: V(b)
Tom: P(b)
Tom: P(a)
Tom: V(b)
Tom: V(a)
Le billet Paris->New York est au nom de: Bill
Le billet New York->Paris est au nom de: Bill

91  Mais...

Il est possible qu'une exécution donne également un interblocage...

% java Serial2
Bill: P(a)
Tom: P(b)
^C

92  Progress graph


Les seules obstructions sont ``au centre'': on peut déformer tout chemin sur l'un des bords.

93  Exemple d'utilisation

L'interface Serializable de JAVA:

94  Simulation de passage par messages

public class MsgQueue {
    Vector queue = new Vector();

    public synchronized void send(Object obj) {
        queue.addElement(obj);  }

    public synchronized Object recv() {
        if (queue.size() == 0)
            return null;
        Object obj = queue.firstElement();
        queue.removeElementAt(0);
        return obj; } }

95  Exemple

public class Process {
    MsgQueue In;
    MsgQueue Out;

    public Process(MsgQueue i, MsgQueue o) {
        In = i;
        Out = o;
    }

96  

    public void send(int x) {
        Out.send(new Integer(x));
        System.out.println(Thread.currentThread().
                           getName()+": send("+x+")"); }

    public int recv() {
        Object x = In.recv();
        while (x == null)
            x = In.recv();
        int res = ((Integer) x).intValue();
        System.out.println(Thread.currentThread().
                           getName()+": recv "+res);
        return res; }
}
Réception bloquante, ``active polling''.

97  Exemple

public class essaiMsg extends Thread{
    Process p;
    public essaiMsg(Process q) {
        p = q; }

    public void run() {
        for (int i=1;; i++) {
            p.send(i);
            p.recv(); } }

98  

    public static void main(String[] args) {
        MsgQueue Canal1 = new MsgQueue();
        MsgQueue Canal2 = new MsgQueue();
        Process Tim = new Process(Canal1, Canal2);
        Process Tom = new Process(Canal2, Canal1);
        new essaiMsg(Tim).start();
        new essaiMsg(Tom).start();
    }
}

99  Résultat

% java essaiMsg
Thread-2: send(1)
Thread-3: send(1)
Thread-3: recv 1
Thread-3: send(2)
Thread-2: recv 1
Thread-2: send(2)
Thread-2: recv 2
Thread-2: send(3)
Thread-3: recv 2
^C

100  Barrières de synchronisation

(ref.: ``Java Threads'', O'Reilly)

    public Barrier(int nThreads) {
        threads2Wait4 = nThreads;
    }

    public synchronized int waitForRest() 
        throws InterruptedException {
        int threadNum = --threads2Wait4;

101  

        if (iex != null) throw iex;
        if (threads2Wait4 <= 0) {
            notifyAll();
            return threadNum;
        }
        while (threads2Wait4 > 0) {
            if (iex != null) throw iex;
            try {
                wait();
            } catch(InterruptedException ex) {
                iex = ex;
                notifyAll();
            }
        }
        return threadNum;
    }

102  

    public synchronized void freeAll() {
        iex = new InterruptedException("Barrier Released 
                                        by freeAll");
        notifyAll();
    }
}

103  Exemple d'utilisation

public class ProcessIt implements Runnable {
    String[] is;
    Barrier bpStart, bp1, bp2, bpEnd;
    public ProcessIt(String[] sources) {
        is = sources;
        bpStart = new Barrier(sources.length);
        bp1 = new Barrier(sources.length);
        bp2 = new Barrier(sources.length);
        bpEnd = new Barrier(sources.length);
        for (int i=0;i<is.length; i++) {
            (new Thread(this)).start(); } }

104  

    public void run() {
        try {
            int i = bpStart.waitForRest();
            doPhaseOne(is[i]);
            bp1.waitForRest();
            doPhaseTwo(is[i]);
            bp2.waitForRest();
            doPhaseThree(is[i]);
            bpEnd.waitForRest();
        } catch(InterruptedException ex) {};
    }

    public void doPhaseOne(String ps) {
    }

105  

    public void doPhaseTwo(String ps) {
    }

    public void doPhaseThree(String ps) {
    }

    static public void main(String[] args) {
        ProcessIt pi = new ProcessIt(args);
    }
}

This document was translated from LATEX by HEVEA.