threads et programmation concurrentew3.uqo.ca/dii/exercices/inf1573/threads.pdf · 1 inf1573 1...

6
1 INF1573 INF1573 1 Threads Threads et programmation et programmation concurrente concurrente INF1573 INF1573 - Programmation II Programmation II Hiver 2007 Hiver 2007 Dr Y.Sami Dr Y.Sami 2 INF1573 INF1573 Threads Threads et programmation et programmation concurrente concurrente Objectifs: Objectifs: - Comprendre le concept de Comprendre le concept de thread thread. - Savoir comment concevoir et Savoir comment concevoir et écrire des crire des programmes avec plusieurs programmes avec plusieurs threads threads. - Être capable d Être capable d’ utiliser la classe utiliser la classe Thread Thread et et l’ interface interface Runnable Runnable. - Comprendre le cycle de vie d Comprendre le cycle de vie d’ un un thread thread. - Savoir comment synchroniser des Savoir comment synchroniser des threads threads. - Appr Apprécier l cier l’ utilisation de l utilisation de l’héritage dans les ritage dans les applications utilisant les applications utilisant les threads threads. 3 INF1573 INF1573 Plan du cours Plan du cours Introduction Introduction Qu Qu’ est est-ce qu ce qu’ un un thread thread ? Les Les états et le cycle de vie d tats et le cycle de vie d’ un un thread thread. L’ utilisation des utilisation des threads threads pour am pour améliorer le liorer le temps de r temps de réponse d ponse d’ une interface. une interface. Étude de cas: Les tude de cas: Les threads threads qui coop qui coopèrent. rent. 4 INF1573 INF1573 Introduction Introduction Faire plus d Faire plus d’ une chose une chose à la fois en alternant la fois en alternant entre ces choses : prendre une cuill entre ces choses : prendre une cuillère de re de céréales, une gorg ales, une gorgée de caf e de café, un bout de tartine. . , un bout de tartine. . . et ainsi de suite jusqu . et ainsi de suite jusqu’à ’à ce que le petit d ce que le petit déjeuner jeuner se termine. se termine. Il y a plusieurs applications o Il y a plusieurs applications où le programme a le programme a besoin de faire plus d besoin de faire plus d’ une chose une chose à la fois ou la fois ou concurremment. concurremment. Dans Java, la programmation concurrente est Dans Java, la programmation concurrente est permise grâce aux permise grâce aux threads threads. . 5 INF1573 INF1573 Qu Qu’ est est- ce qu ce qu’ un un thread thread ? Un Un thread thread (thread thread d’ ex exécution ou cution ou thread thread de contrôle) est une de contrôle) est une quence d quence d’ instructions ex instructions exécutables cutables à l’ int intérieur d rieur d’ un programme. un programme. Une fa Une façon de visualiser un on de visualiser un thread thread est d est d’é ’établir la liste tablir la liste d’ instructions d instructions d’ un programme telle qu un programme telle qu’ elle s elle s’ ex exécute par la CPU. cute par la CPU. Supposons qu Supposons qu’on divise le programme en deux ou plusieurs on divise le programme en deux ou plusieurs threads threads. Chaque . Chaque thread thread aura sa propre s aura sa propre séquence d quence d’instructions. instructions. À l’ int intérieur d rieur d’ un un thread thread, les instructions s , les instructions s’ ex exécutent cutent quentiellement, l quentiellement, l’ une apr une après l s l’autre. autre. Cependant, en alternant entre l Cependant, en alternant entre l’ ex exécution des instructions de cution des instructions de plusieurs plusieurs threads threads, l , l’ordinateur ex ordinateur exécute plusieurs cute plusieurs threads threads concurremment. concurremment. La CPU ex La CPU exécute une instruction cute une instruction à la fois et plusieurs la fois et plusieurs threads threads concurremment. concurremment. La machine virtuelle Java (JVM) est un exemple de programme La machine virtuelle Java (JVM) est un exemple de programme à plusieurs plusieurs threads threads. Les . Les threads threads de JVM r de JVM réalisent les tâches alisent les tâches cessaires cessaires à l’ ex exécution d cution d’un programme Java. L un programme Java. L’ un des un des threads threads est la est la thread thread de ramasse miettes ( de ramasse miettes (Garbage Garbage Collector Collector Thread Thread). ). 6 INF1573 INF1573 L’exécution concurrente de plusieurs threads: La majorité des ordinateurs personnels sont séquentiels (une seule CPU). Il existe des ordinateurs parallèles (plusieurs CPU). Dans les machines séquentielles, la répartition du temps de CPU entre plusieurs programmes est rendu possible grâce à un algorithme d’ordonnancement qui est sous le contrôle du système d’exploitation et de la machine virtuelle Java. Le choix de l’algorithme d’ordonnancement dépend de la plateforme (Windows, Unix, . . .). Une technique d’ordonnancement commune est connue sous le nom de temps partagé (time slicing). Un quantum de temps est alloué à chaque thread. Dans un ordonnancement basé sur les priorités un thread n’est préempté que par un thread de plus haute priorité que lui.

Upload: others

Post on 25-Jul-2020

41 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Threads et programmation concurrentew3.uqo.ca/dii/exercices/inf1573/threads.pdf · 1 INF1573 1 Threads et programmation concurrente INF1573 - Programmation II Hiver 2007 Dr Y.Sami

1

INF1573INF1573 11

ThreadsThreads et programmation et programmation concurrenteconcurrente

INF1573 INF1573 -- Programmation IIProgrammation II

Hiver 2007Hiver 2007

Dr Y.SamiDr Y.Sami

22INF1573INF1573

ThreadsThreads et programmation et programmation concurrenteconcurrente

Objectifs:Objectifs:-- Comprendre le concept de Comprendre le concept de threadthread..-- Savoir comment concevoir et Savoir comment concevoir et éécrire des crire des

programmes avec plusieurs programmes avec plusieurs threadsthreads..-- Être capable dÊtre capable d’’utiliser la classe utiliser la classe ThreadThread et et

ll’’interface interface RunnableRunnable..-- Comprendre le cycle de vie dComprendre le cycle de vie d’’un un threadthread..-- Savoir comment synchroniser des Savoir comment synchroniser des threadsthreads..-- ApprAppréécier lcier l’’utilisation de lutilisation de l’’hhééritage dans les ritage dans les

applications utilisant les applications utilisant les threadsthreads..

33INF1573INF1573

Plan du coursPlan du cours

IntroductionIntroduction

QuQu’’estest--ce quce qu’’un un threadthread ??

Les Les éétats et le cycle de vie dtats et le cycle de vie d’’un un threadthread..

LL’’utilisation des utilisation des threadsthreads pour ampour amééliorer le liorer le temps de rtemps de rééponse dponse d’’une interface.une interface.

ÉÉtude de cas: Les tude de cas: Les threadsthreads qui coopqui coopèèrent.rent.

44INF1573INF1573

IntroductionIntroduction

Faire plus dFaire plus d’’une chose une chose àà la fois en alternant la fois en alternant entre ces choses : prendre une cuillentre ces choses : prendre une cuillèère de re de ccéérrééales, une gorgales, une gorgéée de cafe de caféé, un bout de tartine. . , un bout de tartine. . . et ainsi de suite jusqu. et ainsi de suite jusqu’à’à ce que le petit dce que le petit dééjeuner jeuner se termine.se termine.

Il y a plusieurs applications oIl y a plusieurs applications oùù le programme a le programme a besoin de faire plus dbesoin de faire plus d’’une chose une chose àà la fois ou la fois ou concurremment. concurremment.

Dans Java, la programmation concurrente est Dans Java, la programmation concurrente est permise grâce aux permise grâce aux threadsthreads. .

55INF1573INF1573

QuQu’’estest--ce quce qu’’un un threadthread ??

Un Un threadthread ((threadthread dd’’exexéécution ou cution ou threadthread de contrôle) est une de contrôle) est une ssééquence dquence d’’instructions exinstructions exéécutables cutables àà ll’’intintéérieur drieur d’’un programme. un programme.

Une faUne faççon de visualiser un on de visualiser un threadthread est dest d’é’établir la liste tablir la liste dd’’instructions dinstructions d’’un programme telle quun programme telle qu’’elle selle s’’exexéécute par la CPU. cute par la CPU.

Supposons quSupposons qu’’on divise le programme en deux ou plusieurs on divise le programme en deux ou plusieurs threadsthreads. Chaque . Chaque threadthread aura sa propre saura sa propre sééquence dquence d’’instructions.instructions.

ÀÀ ll’’intintéérieur drieur d’’un un threadthread, les instructions s, les instructions s’’exexéécutent cutent ssééquentiellement, lquentiellement, l’’une aprune aprèès ls l’’autre.autre.

Cependant, en alternant entre lCependant, en alternant entre l’’exexéécution des instructions de cution des instructions de plusieurs plusieurs threadsthreads, l, l’’ordinateur exordinateur exéécute plusieurs cute plusieurs threadsthreadsconcurremment.concurremment.

La CPU exLa CPU exéécute une instruction cute une instruction àà la fois et plusieurs la fois et plusieurs threadsthreadsconcurremment.concurremment.

La machine virtuelle Java (JVM) est un exemple de programme La machine virtuelle Java (JVM) est un exemple de programme ààplusieurs plusieurs threadsthreads. Les . Les threadsthreads de JVM rde JVM rééalisent les tâches alisent les tâches nnéécessaires cessaires àà ll’’exexéécution dcution d’’un programme Java. Lun programme Java. L’’un des un des threadsthreadsest la est la threadthread de ramasse miettes (de ramasse miettes (GarbageGarbage CollectorCollector ThreadThread).).

66INF1573INF1573

L’exécution concurrente de plusieurs threads:

La majorité des ordinateurs personnels sont séquentiels (une seule CPU).

Il existe des ordinateurs parallèles (plusieurs CPU).

Dans les machines séquentielles, la répartition du temps de CPU entre plusieurs

programmes est rendu possible grâce à un algorithme d’ordonnancement qui est sous

le contrôle du système d’exploitation et de la machine virtuelle Java. Le choix de

l’algorithme d’ordonnancement dépend de la plateforme (Windows, Unix, . . .).

Une technique d’ordonnancement commune est connue sous le nom de temps partagé

(time slicing). Un quantum de temps est alloué à chaque thread. Dans un ordonnancement basé sur les priorités un thread n’est préempté que par un thread de

plus haute priorité que lui.

Page 2: Threads et programmation concurrentew3.uqo.ca/dii/exercices/inf1573/threads.pdf · 1 INF1573 1 Threads et programmation concurrente INF1573 - Programmation II Hiver 2007 Dr Y.Sami

2

77INF1573INF1573

Exemple: la classe NumberThread

public class NumberThread extends Thread {int num;public NumberThread(int n) {

num = n;}

public void run() {for (int k=0; k < 10; k++) {

System.out.print(num);}

} }

88INF1573INF1573

public class Numbers {public static void main(String args[]) {

Thread number1, number2, number3, number4, number5; // 5 threadsnumber1 = new Thread(new NumberThread(1)); number1.start(); // Create and //

// start each threadnumber2 = new Thread(new NumberThread(2)); number2.start();number3 = new Thread(new NumberThread(3)); number3.start();number4 = new Thread(new NumberThread(4)); number4.start();number5 = new Thread(new NumberThread(5)); number5.start();

} // main()} // Numbers

Exécuter ce programme avec 10 itérations dans NumberThreadensuite avec 100 itérations (dans NumberThread).

L’ordre et le timing d’exécution des threads est imprévisible.

Une façon de créer des threadsdans Java est de définir une sous classe Thread et de réécrire la méthode run().

99INF1573INF1573

• Une autre façon de créer un thread est de créer une instance de Thread et de lui passer l’objet Runnable qui est tout objet qui implémente l’interface Runnable (c’est àdire la méthode run()).

public class NumberPrinter implements Runnable {int num;public NumberPrinter(int n) {

num = n;}public void run() {

for (int k=0; k < 10; k++) {System.out.print(num);

} }

1010INF1573INF1573

public class Numbers {public static void main(String args[]) {

Thread number1, number2, number3, number4, number5; // 5 threadsnumber1 = new Thread(new NumberPrinter(1)); number1.start(); // Create and // start each threadnumber2 = new Thread(new NumberPrinter(2)); number2.start();number3 = new Thread(new NumberPrinter(3)); number3.start();number4 = new Thread(new NumberPrinter(4)); number4.start();number5 = new Thread(new NumberPrinter(5)); number5.start();

} // main()} // Numbers

L’utilisation de l’interface Runnable pour la création des threads nous permet de convertir une classe existante en un thread. C’est mieux que de redéfinir une classe existante comme sous-classe de thread.

1111INF1573INF1573

Le contrôle d’un thread:Les différentes méthodes de la classe Thread (start(), stop()) peuvent être utilisées pour contôler l’exécution d’un thread.

La priorité d’un thread:La méthode setPriority(int) permet d’assigner à un thread une priorité entre Thread.MIN.PRIORITY et Thread.MAX.PRIORITY. L’utilisation de la méthode setPriority() permet d’avoir un certain contrôle sur les threads. Le thread de plus haute priorité permet de préempter les threads de plus basse priorité.

Une façon de coordonner le comportement de deux threads est de donner à un thread une priorité plus haute qu’un autre.

Un thread de plus haute priorité qui ne rend jamais la CPU peut mettre en état de famine (starvation) les autres processus.

Forcer les threads à dormir: Les deux méthodes yield() et sleep() permettent de libérer la CPU, mais la méthode sleep() empêche le thread d’être réordonnancépendant un certain nombre de millisecondes.

1212INF1573INF1573

La méthode sleep permet d’endormir un thread pendant un certain nombre de millisecondes. La méthode sleep() lève une exception InterruptedException, elle doit donc être dans un bloc Try/catch.

Public void run() {for (int k=0; k<10; k++) {

try {Thread.sleep((long)(Math.random()*1000));

} catch (InterruptedException e) {System.out.println(e.getMessage());

}System.out.print(num);

}}

À moins d’assigner des priorités aux threads ou de les synchroniser, ils fonctionnent de manière complètement asynchrone (l’ordre d’exécution des threadset leur timing est complètement non prévisible).

Page 3: Threads et programmation concurrentew3.uqo.ca/dii/exercices/inf1573/threads.pdf · 1 INF1573 1 Threads et programmation concurrente INF1573 - Programmation II Hiver 2007 Dr Y.Sami

3

1313INF1573INF1573

Les Les éétats et le cycle de vie dtats et le cycle de vie d’’un un threadthread

1414INF1573INF1573

LL’’utilisation des utilisation des threadsthreads pour pour amamééliorer le temps de rliorer le temps de rééponse ponse

dd’’une interfaceune interfaceDDééfinition du problfinition du problèème:me:

Le but est de mesurer la rapiditLe but est de mesurer la rapiditéé àà laquelle llaquelle l’’utilisateur rutilisateur réépond pond ààcertains stimulus.certains stimulus.Le programme utilise deux boutons. Lorsque le bouton Le programme utilise deux boutons. Lorsque le bouton DrawDraw est cliquest cliquéé, , le programme commence le programme commence àà dessiner des points noirs dessiner des points noirs àà des locations des locations alalééatoires dans une zone rectangulaire de latoires dans une zone rectangulaire de l’é’écran. Aprcran. Aprèès un intervalle s un intervalle de temps alde temps alééatoire, des boutons rouges commencent atoire, des boutons rouges commencent àà se dessiner. se dessiner. Ceci constitue la prCeci constitue la préésentation du stimulus. Aussitôt que le stimulus est sentation du stimulus. Aussitôt que le stimulus est prpréésentsentéé, l, l’’utilisateur est sensutilisateur est senséé appuyer sur le bouton appuyer sur le bouton ClearClear pour pour effacer le contenu de la zone rectangulaire. Pour fournir la meseffacer le contenu de la zone rectangulaire. Pour fournir la mesure du ure du temps de rtemps de rééaction de laction de l’’utilisateur, le programme retourne le nombre de utilisateur, le programme retourne le nombre de points rouges qui ont points rouges qui ont ééttéé dessindessinéés avant que ls avant que l’’utilisateur appuie sur le utilisateur appuie sur le bouton bouton ClearClear..

1515INF1573INF1573

Conception à base d’un seul thread.Le programme a besoin de deux classes:- La classe RandomDotApplet: gère l’interface répond aux actions de l’utilisateur en appelant les méthodes de la classe Dotty.- La classe Dotty: Cette classe contient les méthodes draw() et clear().

1616INF1573INF1573

import java.awt.*;import javax.swing.*;import java.awt.event.*;public class RandomDotApplet extends JApplet implements ActionListener {

public final int NDOTS = 10000;private Dotty dotty; // The drawing classprivate JPanel controls = new JPanel();private JPanel canvas = new JPanel();private JButton draw = new JButton("Draw");private JButton clear = new JButton("Clear");

public void init() {getContentPane().setLayout(new BorderLayout());draw.addActionListener(this);clear.addActionListener(this);controls.add(draw);controls.add(clear);canvas.setBorder(BorderFactory.createTitledBorder("Drawing Canvas"));getContentPane().add("North", controls);getContentPane().add("Center", canvas);getContentPane().setSize(400, 400);

} // init()public void actionPerformed(ActionEvent e) {

if (e.getSource() == draw) {dotty = new Dotty(canvas, NDOTS);dotty.draw();

} else {dotty.clear();

}} // actionPerformed()

} // RandomDotApplet

1717INF1573INF1573

import java.awt.*;import javax.swing.*;public class Dotty {

private static final int HREF = 20, VREF = 20, LEN = 200; // Coordinatesprivate JPanel canvas;private int nDots; // Number of dots to drawprivate int nDrawn; // Number of dots drawnprivate int firstRed = 0; // Number of the first red dot public Dotty(JPanel canv, int dots) {

canvas = canv;nDots = dots;

}public void draw() {

Graphics g = canvas.getGraphics();for (nDrawn = 0; nDrawn < nDots; nDrawn++) {

int x = HREF + (int)(Math.random() * LEN);int y = VREF + (int)(Math.random() * LEN); g.fillOval(x, y, 3, 3); // Draw a dotif ((Math.random() < 0.001) && (firstRed == 0)) {

g.setColor(Color.red); // Change color to redfirstRed = nDrawn;

}} //for

} // draw()public void clear() { // Clear screen and report result

Graphics g = canvas.getGraphics();g.setColor(canvas.getBackground());g.fillRect(HREF, VREF, LEN + 3, LEN + 3);System.out.println("Number of dots drawn since first red = " + (nDrawn-firstRed));

} // clear()} // Dotty 1818INF1573INF1573

Le problème avec la version avec un seul thread est qu’aussi longtemps que la méthode draw() s’exécute, le programme ne peut pas répondre au bouton Clear de l’applet.

Page 4: Threads et programmation concurrentew3.uqo.ca/dii/exercices/inf1573/threads.pdf · 1 INF1573 1 Threads et programmation concurrente INF1573 - Programmation II Hiver 2007 Dr Y.Sami

4

1919INF1573INF1573

Conception à base de plusieurs threads: le thread DottyUne façon de palier au problème est d’introduire un deuxième thread (en plus de l’applet) pour faire le dessin.

Le thread qui s’occupe du dessin sera périodiquement interrompu de manière àpermettre à l’applet de réagir aux évènements.

Le fait d’éclater le programme en plusieurs threads n’est pas suffisant pour améliorer le temps de réponse. La coordination des threads est nécessaire.

Une façon de faire est de laisser Dotty dormir (en utilisant sleep()) après chaque point dessiné. Ainsi si un certain évènement attend d’être pris en compte par l’applet, il aura la chance de s’exécuter.

2020INF1573INF1573

import java.awt.*;import javax.swing.*;public class Dotty implements Runnable {

private static final int HREF = 20, VREF = 20, LEN = 200; // Coordinatesprivate JPanel canvas;private int nDots; // Number of dots to drawprivate int nDrawn; // Number of dots drawnprivate int firstRed = 0; // Number of the first red dotprivate boolean isCleared = false; // The panel has been cleared public void run() {

draw();}public Dotty(JPanel canv, int dots) {

canvas = canv;nDots = dots;

}public void draw() {

Graphics g = canvas.getGraphics();for (nDrawn = 0; !isCleared && nDrawn < nDots; nDrawn++) {

int x = HREF + (int)(Math.random() * LEN);int y = VREF + (int)(Math.random() * LEN); g.fillOval(x, y, 3, 3); // Draw a dotif (Math.random() < 0.001 && firstRed == 0) {

g.setColor(Color.red); // Change color to redfirstRed = nDrawn;

}try {

Thread.sleep(1) ; // Sleep for an instant} catch (InterruptedException e) {

System.out.println(e.getMessage());}

} //for} // draw()public void clear() {

isCleared = true;Graphics g = canvas.getGraphics();g.setColor( canvas.getBackground() );g.fillRect(HREF,VREF,LEN+3,LEN+3);g.setColor(Color.black);g.drawString("Dots since first red = " + (nDrawn-firstRed),HREF,VREF+LEN);

// System.out.println("Number of dots drawn since first red = " + (nDrawn-firstRed) );} // clear()

} // Dotty

2121INF1573INF1573

import java.awt.*;import javax.swing.*;import java.awt.event.*;public class RandomDotApplet extends JApplet implements ActionListener {

public final int NDOTS = 10000;private Dotty dotty; // The drawing classprivate Thread dottyThread;private JPanel controls = new JPanel();private JPanel canvas = new JPanel();private JButton draw = new JButton("Draw");private JButton clear = new JButton("Clear");public void init() {

getContentPane().setLayout(new BorderLayout());draw.addActionListener(this);clear.addActionListener(this);controls.add(draw);controls.add(clear);canvas.setBorder(BorderFactory.createTitledBorder("Drawing Canvas"));getContentPane().add("North", controls);getContentPane().add("Center", canvas);getContentPane().setSize(400, 400);

} // init()public void actionPerformed(ActionEvent e) {

if (e.getSource() == draw) {dotty = new Dotty( canvas, NDOTS );dottyThread = new Thread(dotty);dottyThread.start();

} else {dotty.clear();

}} // actionPerformed()

} // RandomDotApplet2222INF1573INF1573

ÉÉtude de cas: Les tude de cas: Les threadsthreads qui qui coopcoopèèrent.rent.

Pour certaines applications, il est Pour certaines applications, il est nnéécessaire de synchroniser et de cessaire de synchroniser et de coordonner le comportement de plusieurs coordonner le comportement de plusieurs threadsthreads afin quafin qu’’ils coopils coopèèrent rent àà la rla rééalisation alisation dd’’une tâche. Plusieurs de ces applications une tâche. Plusieurs de ces applications sont bassont baséées sur le modes sur le modèèle du producteur le du producteur consommateur. Le consommateur. Le threadthread producteur crproducteur créée e un certain run certain réésultat et le sultat et le threadthreadconsommateur utilise ce rconsommateur utilise ce réésultat.sultat.

2323INF1573INF1573

Définition du problème:

Nous voulons simuler une boulangerie où chaque client qui se présente doit prendre un numéro. Ces numéros sont utilisés pour gérer la file. Le distributeur du pain va ensuite annoncé un numéro avant de servir un client. Le client qui se présente doit détenir le même numéro. Les numéros sont annoncés selon l’ordre croissant, ce qui permet aux clients d’être servis selon leur ordre d’arrivée.

La simulation utilise quatre classes:

-Bakery: responsable de la création des threads et de l’amorcement de la simulation.-TakeANumber: représente le gadget qui garde trace du prochain client à servir.-Clerck: Va utiliser TakeANumber pour déterminer le prochain client et va le servir.-Customer: représente les clients qui vont utilser TakeANumber pour prendre leur place dans le file.

2424INF1573INF1573

Page 5: Threads et programmation concurrentew3.uqo.ca/dii/exercices/inf1573/threads.pdf · 1 INF1573 1 Threads et programmation concurrente INF1573 - Programmation II Hiver 2007 Dr Y.Sami

5

2525INF1573INF1573

La classe TakeANumber:Cette classe doit garder trace de deux choses:-Quel est le numéro du client qui sera prochainement servi.-Quel est le numéro qui sera attribué au prochain client qui se présente.class TakeANumber {

private int next = 0; // Next place in lineprivate int serving = 0; // Next customer to serve

public synchronized int nextNumber() {next = next + 1;return next;

} // nextNumber()

public int nextCustomer() {++serving;return serving;

} // nextCustomer()

} // TakeANumber

La méthode nextNumber() est déclarée synchronized. Ceci assure qu’un seul thread àla fois exécute cette méthode. Ceci assure l’exclusion mutuelle de l’accès de plusieurs clients à un même objet: TakeANumber. Ceci évite d’attribuer un même numéro à deux clients différents.

2626INF1573INF1573

public class Customer extends Thread {private static int number = 10000; // Initial ID numberprivate int id;private TakeANumber takeANumber;

public Customer( TakeANumber gadget ) {id = ++number;takeANumber = gadget;

}

public void run() {try {

sleep( (int)(Math.random() * 1000 ) );System.out.println("Customer " + id + " takes ticket "+ takeANumber.nextNumber());

} catch (InterruptedException e) {System.out.println("Exception " + e.getMessage());

} } // run()

} // Customer

Les variables static sont associées aux classes elles même et non à ses instances. Elles sont souvent utilisées pour associer un identificateur unique à toute instance d’une classe.

2727INF1573INF1573

public class Clerk extends Thread {private TakeANumber takeANumber; public Clerk(TakeANumber gadget) {

takeANumber = gadget;}public void run() {

while (true) {try {

sleep( (int)(Math.random() * 50));System.out.println("Clerk serving ticket " + takeANumber.nextCustomer());

} catch (InterruptedException e) {System.out.println("Exception " + e.getMessage() );

} } //while

} //run()} // Clerk

La méthode sleep est nécessaire pour permettre aux threads Customer de s’exécuter.

2828INF1573INF1573

public class Bakery {public static void main(String args[]) {

System.out.println( "Starting clerk and customer threads" );TakeANumber numberGadget = new TakeANumber();Clerk clerk = new Clerk(numberGadget);clerk.start();for (int k = 0; k < 5; k++) {

Customer customer = new Customer(numberGadget);customer.start();

}} // main()

} // Bakery

Nous risquons avec cette solution de servir des clients inexistants.Pour résoudre ce problème nous rajoutons une méthode customerwaiting() àTakeANumber. Cette méthode doit retourner true si next>serving.

2929INF1573INF1573

class TakeANumber {private int next = 0; // Next place in lineprivate int serving = 0; // Next customer to serve

public synchronized int nextNumber() {next = next + 1;return next;

} // nextNumber()

public int nextCustomer() {++serving;return serving;

} // nextCustomer()

public boolean customerWaiting() {return next > serving;

}} // TakeANumber

3030INF1573INF1573

public class Clerk extends Thread {private TakeANumber takeANumber; public Clerk(TakeANumber gadget) {

takeANumber = gadget;}

public void run() {while (true) {

try {sleep((int)(Math.random() * 50));if (takeANumber.customerWaiting())

System.out.println("Clerk serving ticket " + takeANumber.nextCustomer());} catch (InterruptedException e) {

System.out.println("Exception " + e.getMessage() );}

} // while} // run()

} // Clerk

Page 6: Threads et programmation concurrentew3.uqo.ca/dii/exercices/inf1573/threads.pdf · 1 INF1573 1 Threads et programmation concurrente INF1573 - Programmation II Hiver 2007 Dr Y.Sami

6

3131INF1573INF1573

Le problème avec la solution précédente est qu’un Customer peut être interrompu entre le moment où il prend un numéro de TakeANumber et le moment où il l’affiche.

Une section critique est une section d’un thread qui ne doit pas être interrompue lors de l’exécution.

Pour résoudre le problème précédent, il faut traiter toutes les actions de TakeANumber comme sections critiques. Les deux méthodes de TakeANumberdoivent être déclarées synchronized.

3232INF1573INF1573

public class TakeANumber {private int next = 0; // Next place in lineprivate int serving = 0; // Next customer to serve

public synchronized int nextNumber(int custId) {next = next + 1;System.out.println( "Customer " + custId + " takes ticket " + next );return next;

}

public synchronized int nextCustomer() {++serving;System.out.println(" Clerk serving ticket " + serving );return serving;

}

public synchronized boolean customerWaiting() {return next > serving;

}} // TakeANumber

3333INF1573INF1573

public class Clerk extends Thread {private TakeANumber takeANumber;

public Clerk(TakeANumber gadget) {takeANumber = gadget;

}public void run() {// for (int k = 0; k < 10; k++) {

while (true) {try {

sleep( (int)(Math.random() * 1000));if (takeANumber.customerWaiting())

takeANumber.nextCustomer();} catch (InterruptedException e) {

System.out.println("Exception: " + e.getMessage());}

} // for} // run()

} // Clerk

3434INF1573INF1573

public class Customer extends Thread {

private static int number = 10000; // Initial ID numberprivate int id;private TakeANumber takeANumber;

public Customer( TakeANumber gadget ) {id = ++number;takeANumber = gadget;

}

public void run() {try {

sleep((int)(Math.random() * 2000));takeANumber.nextNumber(id);

} catch (InterruptedException e) {System.out.println("Exception: " + e.getMessage() );

} } // run()

} // Customer

3535INF1573INF1573

Le problème avec la solution précédente est que le thread Clerk est busywaiting, il vérifie constamment s’il y a un Customer pour le servir. Il consomme inutilement le temps de CPU.

Une solution serait de le forcer à attendre (en utilisant wait()) jusqu’à ce que un Customer arrive. Dés qu’un Customer est prêt, il doit réveiller Clerck en utilisant la méthode notify().

Les modifications à apporter sont:

-la méthode nextCustomer en utilisant wait().-La méthode nextNumber en utilisant notify().-La suppression de customerWaiting() dans la méthode run() de Clerk

Référence:

Ralph Morelli, Object oriented problem solving Java, Java, Java, Prentice Hall 2002.