Threads et programmation concurrente. concurrente. Plan du cours. INF Programmation II Hiver 2007 Dr Y.Sami INF1573. Introduction Qu est

Documents pareils
Un ordonnanceur stupide

Threads. Threads. USTL routier 1

Synchro et Threads Java TM

J2SE Threads, 1ère partie Principe Cycle de vie Création Synchronisation

Introduction à Java. Matthieu Herrb CNRS-LAAS. Mars

Introduction : les processus. Introduction : les threads. Plan

Info0604 Programmation multi-threadée. Cours 5. Programmation multi-threadée en Java

INITIATION AU LANGAGE JAVA

Exclusion Mutuelle. Arnaud Labourel Courriel : arnaud.labourel@lif.univ-mrs.fr. Université de Provence. 9 février 2011

Notion de thread (1/2)

Interfaces graphiques avec l API Swing

TD2 Programmation concurrentielle

NFP 121. Java et les Threads. Présentation : Thierry Escalarasse Mai 2007

Développement Logiciel

Premiers Pas en Programmation Objet : les Classes et les Objets

La JVM. La machine virtuelle Java. La JVM. La JVM

Java Licence Professionnelle CISII,

IFT287 Exploitation de base de données relationnelles et orientées objet. Laboratoire Mon premier programme Java en Eclipse

Programme Compte bancaire (code)

Problèmes liés à la concurrence

Introduction à la programmation concurrente

Cette application développée en C# va récupérer un certain nombre d informations en ligne fournies par la ville de Paris :

Info0101 Intro. à l'algorithmique et à la programmation. Cours 3. Le langage Java

as Architecture des Systèmes d Information

Plan du cours. Historique du langage Nouveautés de Java 7

Programmer en JAVA. par Tama

Cours intensif Java. 1er cours: de C à Java. Enrica DUCHI LIAFA, Paris 7. Septembre Enrica.Duchi@liafa.jussieu.fr

4. Outils pour la synchronisation F. Boyer, Laboratoire Lig

Remote Method Invocation Les classes implémentant Serializable

Programmation par les Objets en Java

Projet de programmation (IK3) : TP n 1 Correction

Développement mobile MIDP 2.0 Mobile 3D Graphics API (M3G) JSR 184. Frédéric BERTIN

Package Java.util Classe générique

Objets et Programmation. origine des langages orientés-objet

TP1 : Initiation à Java et Eclipse

2. Comprendre les définitions de classes

Calcul Parallèle. Cours 5 - JAVA RMI

TD Objets distribués n 3 : Windows XP et Visual Studio.NET. Introduction à.net Remoting

LMI 2. Programmation Orientée Objet POO - Cours 9. Said Jabbour. jabbour@cril.univ-artois.fr

Cours de Systèmes d Exploitation

Corrigé des exercices sur les références

Chapitre I Notions de base et outils de travail

ACTIVITÉ DE PROGRAMMATION

Introduction au langage Java

TP3. Mail. Attention aux fausses manoeuvres lors de ce TP vous pouvez endommager votre mail sur ouindose.

Structure d un programme et Compilation Notions de classe et d objet Syntaxe

Programmation Réseau. Sécurité Java. UFR Informatique jeudi 4 avril 13

Programmation Par Objets

PIGOURIER Vincent ANNEE SPECIALE 99/00 RAPPORT DE PROJET : LES THREADS JAVA. Responsable : Serge Rouveyrol

INTRODUCTION AUX SYSTEMES D EXPLOITATION. TD2 Exclusion mutuelle / Sémaphores

Java Licence Professionnelle Cours 7 : Classes et méthodes abstraites

INTRODUCTION A JAVA. Fichier en langage machine Exécutable

Remote Method Invocation (RMI)

Exceptions. 1 Entrées/sorties. Objectif. Manipuler les exceptions ;

Java DataBaseConnectivity

Apprendre la Programmation Orientée Objet avec le langage Java (avec exercices pratiques et corrigés)

Java - la plateforme

Une introduction à Java

Derrière toi Une machine virtuelle!

Chapitre 10. Les interfaces Comparable et Comparator 1

Langage et Concepts de ProgrammationOrientée-Objet 1 / 40

Tp 1 correction. Structures de données (IF2)

Communication inter-processus (IPC) : tubes & sockets. exemples en C et en Java. F. Butelle

Cours 2 : programmation des interfaces graphiques

Encapsulation. L'encapsulation consiste à rendre les membres d'un objet plus ou moins visibles pour les autres objets.

Création d un service web avec NetBeans 5.5 et SJAS 9

Cours 2: Exclusion Mutuelle entre processus (lourds, ou légers -- threads)

TD3: tableaux avancées, première classe et chaînes

Langage Java. Classe de première SI

Auto-évaluation Programmation en Java

Généralités sur le Langage Java et éléments syntaxiques.

Java Licence Professionnelle CISII,

SugarCubes. Jean-Ferdinand Susini Maître de Conférences, CNAM Chaire systèmes enfouis et embarqués. Paris, le 9 janvier, 2009

Dis papa, c est quoi un bus logiciel réparti?

Exercices INF5171 : série #3 (Automne 2012)

PROGRAMMATION PAR OBJETS

Les processus légers : threads. Système L3, /31

Cours 1: Java et les objets

Programmation avec des objets : Cours 7. Menu du jour

Une introduction à la technologie EJB (2/3)

On appelle variable condition une var qui peut être testée et

Programmation Objet Java Correction

Environnements de développement (intégrés)

Modèle à composants. Daniel Hagimont. IRIT/ENSEEIHT 2 rue Charles Camichel - BP TOULOUSE CEDEX 7. Remerciements

Cours 1 : Introduction. Langages objets. but du module. contrôle des connaissances. Pourquoi Java? présentation du module. Présentation de Java

Initiation à JAVA et à la programmation objet.

TP n 2 Concepts de la programmation Objets Master 1 mention IL, semestre 2 Le type Abstrait Pile

JADE : Java Agent DEvelopment framework. Laboratoire IBISC & Départ. GEII Université & IUT d Evry nadia.abchiche@ibisc.univ-evry.

Serveur d'archivage 2007 Installation et utilisation de la BD exist

Prénom : Matricule : Sigle et titre du cours Groupe Trimestre INF1101 Algorithmes et structures de données Tous H2004. Loc Jeudi 29/4/2004

Projet gestion d'objets dupliqués

Bases Java - Eclipse / Netbeans

École Polytechnique de Montréal. Département de Génie Informatique et Génie Logiciel. Cours INF2610. Contrôle périodique.

Processus! programme. DIMA, Systèmes Centralisés (Ph. Mauran) " Processus = suite d'actions = suite d'états obtenus = trace

1/24. I passer d un problème exprimé en français à la réalisation d un. I expressions arithmétiques. I structures de contrôle (tests, boucles)

Par Laurent DESECHALLIERS. Mastère Spécialisé en Management de Projets en Milieu Industriel. CESI de Rouen Promotion 2002/2003.

Gestion distribuée (par sockets) de banque en Java

1. Structure d un programme C. 2. Commentaire: /*..texte */ On utilise aussi le commentaire du C++ qui est valable pour C: 3.

LOG4430 : Architecture logicielle et conception avancée

Programmation Orientée Objet - Licence TIS CM8/9. Rappel sur la séance précédente. Lancelot Pecquet Lancelot.Pecquet@math.univ-poitiers.

Transcription:

Threads et programmation concurrente - Programmation II Hiver 2007 Dr Y.Sami Threads et programmation concurrente Objectifs: - Comprendre le concept de thread. - Savoir comment concevoir et écrire des programmes avec plusieurs threads. - Être capable d utiliser d la classe Thread et l interface Runnable. - Comprendre le cycle de vie d un d thread. - Savoir comment synchroniser des threads. - Apprécier l utilisation l de l héritage l dans les applications utilisant les threads. 1 2 Plan du cours Introduction Introduction Qu est est-ce qu un un thread? Les états et le cycle de vie d un d thread. L utilisation des threads pour améliorer le temps de réponse r d une d interface. Étude de cas: Les threads qui coopèrent. Faire plus d une d chose à la fois en alternant entre ces choses : prendre une cuillère de céréales, une gorgée e de café,, un bout de tartine... et ainsi de suite jusqu à ce que le petit déjeuner d se termine. Il y a plusieurs applications oùo le programme a besoin de faire plus d une d chose à la fois ou concurremment. Dans Java, la programmation concurrente est permise grâce aux threads. 3 4 Qu est est-ce qu un un thread? Un thread (thread d exécution ou thread de contrôle) est une séquence d instructions d exécutables à l intérieur d un d programme. Une façon de visualiser un thread est d éd établir la liste d instructions d un d programme telle qu elle s exs exécute par la CPU. Supposons qu on divise le programme en deux ou plusieurs threads.. Chaque thread aura sa propre séquence s d instructions. d À l intérieur d un d thread,, les instructions s exs exécutent séquentiellement, l une l après s l autre. l Cependant, en alternant entre l exl exécution des instructions de plusieurs threads,, l ordinateur l exécute plusieurs threads concurremment. La CPU exécute une instruction à la fois et plusieurs threads concurremment. La machine virtuelle Java (JVM) est un exemple de programme à plusieurs threads.. Les threads de JVM réalisent r les tâches nécessaires à l exécution d un d programme Java. L un L des threads est la thread de ramasse miettes (Garbage( Collector Thread). 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. 5 6 1

Exemple: la classe NumberThread public class NumberThread extends Thread { int num; public NumberThread(int n) { num = n; for (int k=0; k < 10; k++) { System.out.print(num); public class Numbers { public static void main(string args[]) { Thread number1, number2, number3, number4, number5; // 5 threads number1 = new Thread(new NumberThread(1)); number1.start(); // Create and // // start each thread number2 = 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 NumberThread ensuite avec 100 itérations (dans NumberThread). L ordre et le timing d exécution des threads est imprévisible. 7 Une façon de créer des threads dans Java est de définir une sous classe Thread et de réécrire la méthode run(). 8 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; for (int k=0; k < 10; k++) { System.out.print(num); public class Numbers { public static void main(string args[]) { Thread number1, number2, number3, number4, number5; // 5 threads number1 = new Thread(new NumberPrinter(1)); number1.start(); // Create and // start each thread number2 = 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. 9 10 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. 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++) { 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 threads et leur timing est complètement non prévisible). 11 12 2

Les états et le cycle de vie d un d thread L utilisation des threads pour améliorer le temps de réponse r d une interface Définition du problème: Le but est de mesurer la rapidité à laquelle l utilisateur l répond r à certains stimulus. Le programme utilise deux boutons. Lorsque le bouton Draw est cliqué, le programme commence à dessiner des points noirs à des locations aléatoires atoires dans une zone rectangulaire de l él écran. Après s un intervalle de temps aléatoire, atoire, des boutons rouges commencent à se dessiner. Ceci constitue la présentation du stimulus. Aussitôt que le stimulus est présent senté,, l utilisateur l est sensé appuyer sur le bouton Clear pour effacer le contenu de la zone rectangulaire. Pour fournir la mesure du temps de réaction r de l utilisateur, l le programme retourne le nombre de points rouges qui ont été dessinés s avant que l utilisateur l appuie sur le bouton Clear. 13 14 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(). 15 import java.awt.event.*; public class RandomDotApplet extends JApplet implements ActionListener { public final int NDOTS = 10000; private Dotty dotty; // The drawing class 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); dotty.draw(); else { dotty.clear(); // actionperformed() // RandomDotApplet 16 public class Dotty { private static final int HREF = 20, VREF = 20, LEN = 200; // Coordinates private JPanel canvas; private int ndots; // Number of dots to draw private int ndrawn; // Number of dots drawn private int firstred = 0; // Number of the first red dot public Dotty(JPanel canv, int dots) { canvas = canv; ndots = dots; public void draw() { 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 dot Le problème avec la version avec un seul thread est qu aussi longtemps que la if ((Math.random() < 0.001) && (firstred == 0)) { méthode draw() s exécute, le programme ne peut pas répondre au bouton Clear de g.setcolor(color.red); // Change color to red firstred = ndrawn; l applet. //for // draw() public void clear() { // Clear screen and report result 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 17 18 3

Conception à base de plusieurs threads: le thread Dotty Une 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. 19 public class Dotty implements Runnable { private static final int HREF = 20, VREF = 20, LEN = 200; // Coordinates private JPanel canvas; private int ndots; // Number of dots to draw private int ndrawn; // Number of dots drawn private int firstred = 0; // Number of the first red dot private boolean iscleared = false; // The panel has been cleared draw(); public Dotty(JPanel canv, int dots) { canvas = canv; ndots = dots; public void draw() { 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 dot if (Math.random() < 0.001 && firstred == 0) { g.setcolor(color.red); // Change color to red firstred = ndrawn; Thread.sleep(1) ; // Sleep for an instant catch (InterruptedException e) { System.out.println(e.getMessage()); //for // draw() public void clear() { iscleared = true; 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 20 import java.awt.event.*; public class RandomDotApplet extends JApplet implements ActionListener { public final int NDOTS = 10000; private Dotty dotty; // The drawing class private 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() // RandomDotApplet 21 Étude de cas: Les threads qui coopèrent. Pour certaines applications, il est nécessaire de synchroniser et de coordonner le comportement de plusieurs threads afin qu ils coopèrent à la réalisation r d une tâche. Plusieurs de ces applications sont basées sur le modèle du producteur consommateur. Le thread producteur crée un certain résultat r et le thread consommateur utilise ce résultat. r 22 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. 23 24 4

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 line private 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 public class Customer extends Thread { private static int number = 10000; // Initial ID number private int id; public Customer( TakeANumber gadget ) { id = ++number; 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 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 25 clients différents. 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. 26 public class Clerk extends Thread { public Clerk(TakeANumber gadget) { while (true) { 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. 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. 27 28 class TakeANumber { private int next = 0; // Next place in line private 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 public class Clerk extends Thread { public Clerk(TakeANumber gadget) { while (true) { 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 29 30 5

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 TakeANumber doivent être déclarées synchronized. public class TakeANumber { private int next = 0; // Next place in line private 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 31 32 public class Clerk extends Thread { public Clerk(TakeANumber gadget) { // for (int k = 0; k < 10; k++) { while (true) { sleep( (int)(math.random() * 1000)); if (takeanumber.customerwaiting()) takeanumber.nextcustomer(); catch (InterruptedException e) { System.out.println("Exception: " + e.getmessage()); // for // run() // Clerk public class Customer extends Thread { private static int number = 10000; // Initial ID number private int id; public Customer( TakeANumber gadget ) { id = ++number; sleep((int)(math.random() * 2000)); takeanumber.nextnumber(id); catch (InterruptedException e) { System.out.println("Exception: " + e.getmessage() ); // run() // Customer 33 34 Le problème avec la solution précédente est que le thread Clerk est busy waiting, 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. 35 6