import java.util.concurrent.atomic.AtomicLong; // V0.3 - PM 17/09/21 interface Incrémenteur extends Runnable{ void incr(); /* Boucle d'incrémentation effectuée par un processus (thread). * Le principe/objectif est de réaliser cette boucle, comportant un même nombre * d'incrémentations en suivant différents schémas pour maintenir la cohérence * (ou pas) et de comparer ces schémas en termes de correction et d'efficacité */ } public class IncrMes { // static long cpt = 0L; static volatile long cpt = 0L; // static final long NB_IT = 1000L; //Nb d'itérations de la boucle externe // static final long NB_IT_INTERNES = 1000000L; //Nb d'itérations de la boucle interne static final long NB_IT = 10L; //Nb d'itérations de la boucle externe static final long NB_IT_INTERNES = 100L; //Nb d'itérations de la boucle interne static long Attente_ms = 2L; static final int Attente_nano = 0; static Thread[] activités; // Tableau des processus static Object mutex = new Object(); // pour les blocs synchronized static void lancer(int nbA, Incrémenteur r) { // Initialisation du/des compteur(s) incrémentés cpt = 0L; /* Création et lancement des threads. * Chaque thread exécute le même code, qui est la méthode run() d'une classe * implantant un Incrémenteur (autrement dit un schéma de réalisation particulier * de la boucle dincrémentation) */ for (int i = 0; i < nbA ; i++) { try { IncrMes.activités[i] = new Thread(r,"t"+i); } catch (Exception e) { System.out.println(e); } IncrMes.activités[i].start(); } } static void finir() { // Attente de la terminaison des différents threads lancés for (int i = 0; i < IncrMes.activités.length ; i++) { try { IncrMes.activités[i].join(); } catch (InterruptedException e) { System.out.println(e); } } } public static void main(String[] args) { int nbActivités = 3; int nbMesures = 6; int i = 0; long départ, fin; boolean mutex = true; // Chargement des paramètres saisis en ligne de commande if (args.length == 3) { try { nbActivités = Integer.parseInt (args[0]); Attente_ms = Integer.parseInt (args[1]); nbMesures = Integer.parseInt (args[2]); } catch (NumberFormatException nfx) { Attente_ms = 0; } if ((nbActivités < 1) || (Attente_ms < 1) || (nbMesures < 1)) { System.out.println("Usage : IncrMes ," +"avec Nb activités, durée pause, nbMesures >= 1"); System.exit (1); } } else { System.out.println("Nb d'arguments ≠ 3. Exécution avec 6 mesures, 3 activités et 2 ms de pause"); } activités = new Thread[nbActivités]; for (int it=1; it<=nbMesures; it++) { // // Mesure itérée, pour éviter des écarts trop importants sur les premières mesures // (du fait de possibles optimisations : processeur, cache, compilateur). // Idée = retenir une moyenne des dernières mesures. // Boucle séquentielle : NB_IT x NB_IT_INTERNES x nbActivités itérations cpt=0L; départ = System.nanoTime(); for (long li = 0; li < nbActivités*IncrMes.NB_IT; li++) { for (long j = 1; j<=IncrMes.NB_IT_INTERNES; j++) { IncrMes.cpt=IncrMes.cpt+j/j; // j/j pour tenter d'éviter les optimisations du compilateur } try { Thread.sleep(IncrMes.Attente_ms, IncrMes.Attente_nano); // sleep pour modéliser un temps de calcul sans conflit d'accès et... // pour tenter d'éviter les optimisations du cache } catch (InterruptedException ie) { System.out.println("InterruptedException : " + ie); } } fin = System.nanoTime(); //System.out.print((fin-départ)/1000000L + ", "); System.out.println("Durée exécution mono : " + (fin-départ)/1000000L); System.out.println(); // Exemple de mesure avec IncrémenteurNonSync départ = System.nanoTime(); // Lancement de nbActivités processus effectuant // chacun NB_IT x NB_IT_INTERNES itérations lancer(nbActivités, new IncrémenteurNonSync()); finir(); fin = System.nanoTime(); //System.out.print((fin-départ)/1000000L + ", "); System.out.println("Durée exécution non synchronisée (ms): " + (fin-départ)/1000000L); System.out.println(); // Compléter ici, sur le modèle précédent (lignes 112-120, (ou éditer la ligne 116) //en définissant et mesurant différents Incrémenteur // de manière analogue à ce qui est fait pour IncrémenteurNonSync } } } class IncrémenteurNonSync implements Incrémenteur { // class IncrémenteurNonSync extends AtomicLong implements Incrémenteur { /* Exemple d'incrémenteur : -la méthode incr effectue les incrémentations sans gérer les conflis d'accès au compteur - la méthode run() appelle incr() */ public synchronized void incr_sync(long j){ IncrMes.cpt=IncrMes.cpt+j/j; } public synchronized void interne(long i) { for (long j = 1; j<=IncrMes.NB_IT_INTERNES; j++) { // incrémentation élémentaire IncrMes.cpt=IncrMes.cpt+j/j; } } public void incr() { // boucle imbriquée pour permettre (éventuellement) de tester différents // grains de synchronisation for (long i = 0L; i < IncrMes.NB_IT; i++) { // boucle interne for (long j = 1; j<=IncrMes.NB_IT_INTERNES; j++) { // incrémentation élémentaire incr_sync(j); // IncrMes.cpt=IncrMes.cpt+j/j; } // interne(i); try { Thread.sleep(IncrMes.Attente_ms, IncrMes.Attente_nano); } // Attente depour éviter l'utilisation du cache et modéliser // une partie du calcul sans conflit. // Vous pouvez (éventuellement) commenter l'attente // pour voir l'effet (intéressant) du mécanisme de cache, // ou encore modifier la valeur de l'attente pour voir // l'effet du grain de l'exclusion mutuelle sur les temps // d'exécution catch (InterruptedException ie) { System.out.println("InterruptedException : " + ie); } } } public void run() { // afficher éventuellement la valeur du compteur avant/après // pour vérifier la cohérence des incrémentations System.out.println("Thread " + Thread.currentThread().getName() + " part de : " + IncrMes.cpt); this.incr(); System.out.println("Thread " + Thread.currentThread().getName() + " finit à : " + IncrMes.cpt); } }