CentraleSupélecDépartement informatique
Plateau de Moulon
3 rue Joliot-Curie
F-91192 Gif-sur-Yvette cedex
Une solution possible de l'exercice threads et synchronisation

Solution threads

EssaiThread.java

package gloo.synchronisation;

public class EssaiThread extends Thread {

    public EssaiThread( String name ) {
        super( name );
    }

    @Override
    public void run() {
        System.out.println( getName() + ": started" );
        try {
            Thread.sleep(( int ) ( Math.random() * 1000 ));
        }
        catch( InterruptedException e ) {}
        System.out.println( getName() + ": finished" );
    }

    public static void main( String[] args ) {
        System.out.println( "main(): started" );
        EssaiThread t = new EssaiThread( "EssaiThread" );
        t.start();
        System.out.println( "main(): waiting" );
        try {
            t.join();
        }
        catch( InterruptedException e ) {}
        System.out.println( "main(): finished" );
    }
}

EssaiRunnable.java

package gloo.synchronisation;

public class EssaiRunnable implements Runnable {

    private static Object monitor;

    private String name;

    public EssaiRunnable( String name ) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println( name + ": started" );
        try {
            Thread.sleep(( int ) ( Math.random() * 1000 ));
        }
        catch( InterruptedException e ) {}
        synchronized( monitor ) {
            System.out.println( name + ": I am the owner of the monitor" );
            monitor.notify();
            System.out.println( name + ": I free the monitor" );
        }
        System.out.println( name + ": finished" );
    }

    public static void main( String[] args ) {
        System.out.println( "main() started" );
        monitor = new Object();
        EssaiRunnable r = new EssaiRunnable( "EssaiRunnable" );
        Thread t = new Thread( r );
        t.start();
        synchronized( monitor ) {
            System.out.println( "main() waiting" );
            try {
                System.out.println( "main: before wait(), I am the owner of the monitor, but wait() will free it" );
                monitor.wait();
                System.out.println( "main: after wait(),  I am again the owner of the monitor, I will free it" );
            }
            catch( InterruptedException e ) {}
        }
        System.out.println( "main() finished" );
    }
}

BoiteALettres.java

package gloo.synchronisation;

/*
 * Version sans synchronisation
 */
public class BoiteALettres {

    private int[] boite;
    private int indiceDepot = 0;
    private int indiceRetrait = 0;

    public BoiteALettres( int taille ) {
        this.boite = new int[taille];
    }

    public void depot( int val ) {
        boite[indiceDepot] = val;
        indiceDepot = ( indiceDepot + 1 ) % boite.length;
    }

    public int retrait() {
        int val = boite[indiceRetrait];
        boite[indiceRetrait] = 0;
        indiceRetrait = ( indiceRetrait + 1 ) % boite.length;
        return val;
    }
}

Producteur.java

package gloo.synchronisation;

public class Producteur extends Thread {
    private BoiteALettres boite;
    private int nombreMessages;

    public Producteur( BoiteALettres boite, int nombreMessages ) {
        super();
        this.boite = boite;
        this.nombreMessages = nombreMessages;
    }

    public void run() {
        for( int i = 1; i <= nombreMessages; ++i ) {
            try {
                Thread.sleep(( int ) ( Math.random() * 1000 ));
            }
            catch( InterruptedException e ) {}
            System.out.println( "Depot de " + i );
            boite.depot( i );
        }
    }
}

Consommateur.java

package gloo.synchronisation;

public class Consommateur extends Thread {
    private BoiteALettres boite;
    private int nombreMessages;

    public Consommateur( BoiteALettres boite, int nombreMessages ) {
        super();
        this.boite = boite;
        this.nombreMessages = nombreMessages;
    }

    public void run() {
        for( int i = 1; i <= nombreMessages; ++i ) {
            try {
                Thread.sleep(( int ) ( Math.random() * 1000 ));
            }
            catch( InterruptedException e ) {}
            int val = boite.retrait();
            if( val <= 0 ) {
                System.out.println( "Erreur lors du retrait : " + val + " obtenu" );
            }
            else {
                System.out.println( "Retrait de " + val );
            }
        }
    }
}

BoiteALettresAvecMoniteur.java

package gloo.synchronisation;

/*
 * Version suspendant le consommateur si la boite est vide
 * et le producteur si la boite est pleine
 */
public class BoiteALettresAvecMoniteur extends BoiteALettres {

    public BoiteALettresAvecMoniteur( int taille ) {
        super( taille );
    }

    private int nombreMessages = 0;

    @Override
    public synchronized void depot( int val ) {
        try {
            // Si vous utilisez un if ici, il y aura des problèmes
            // avec plusieurs producteurs
            // getTaille : méthode ajoutée à BoiteALettres
            while( nombreMessages == getTaille() ) {
                System.out.println( "Le producteur attend" );
                wait();
                System.out.println( "Le producteur est reveille" );
            }
        }
        catch( InterruptedException e ) {
            e.printStackTrace();
        }
        super.depot( val );
        ++nombreMessages;
        notify();
    }

    @Override
    public synchronized int retrait() {
        try {
            // Si vous utilisez un if ici, il y aura des problèmes
            // avec plusieurs consommateurs
            while( nombreMessages == 0 ) {
                System.out.println( "Le consommateur attend" );
                wait();
                System.out.println( "Le consommateur est reveille" );
            }
        }
        catch( InterruptedException e ) {
            e.printStackTrace();
        }
        --nombreMessages;
        notify();
        return super.retrait();
    }
}

Main.java (V1)

package gloo.synchronisation;

/*
 * Version 1 producteur et 1 consommateur
 */
public class Main {

    private static final int TAILLE_BOITE = 2;
    private static final int NOMBRE_MESSAGES = 10;

    public static void main( String[] args ) {
        BoiteALettres boite = new BoiteALettresAvecMoniteur( TAILLE_BOITE );
        Producteur p = new Producteur( boite, NOMBRE_MESSAGES );
        Consommateur c = new Consommateur( boite, NOMBRE_MESSAGES );
        p.start();
        c.start();
        try {
            p.join();
            c.join();
        }
        catch( InterruptedException e ) {}
    }
}

Main.java (V2)

package gloo.synchronisation;

/*
 * Version 2 producteurs et 1 consommateur
 */
public class Main {

    private static final int TAILLE_BOITE = 2;
    private static final int NOMBRE_MESSAGES = 10;

    public static void main( String[] args ) {
        BoiteALettres boite = new BoiteALettresAvecMoniteur( TAILLE_BOITE );
        Producteur p1 = new Producteur( boite, NOMBRE_MESSAGES );
        Producteur p2 = new Producteur( boite, NOMBRE_MESSAGES );
        Consommateur c = new Consommateur( boite, NOMBRE_MESSAGES * 2 );
        p1.start();
        p2.start();
        c.start();
        try {
            p1.join();
            p2.join();
            c.join();
        }
        catch( InterruptedException e ) {}
    }
}

BoiteALettresAvecSemaphore.java (V1)

package gloo.synchronisation;

import java.util.concurrent.Semaphore;

public class BoiteALettresAvecSemaphore extends BoiteALettres {

    private Semaphore placesLibres;
    private Semaphore placesOccupees;

    public BoiteALettresAvecSemaphore( int taille ) {
        super( taille );

        placesLibres = new Semaphore( taille );
        placesOccupees = new Semaphore( 0 );
    }

    @Override
    public void depot( int val ) {
        try {
            placesLibres.acquire();
        }
        catch( InterruptedException e ) {}
        super.depot( val );
        placesOccupees.release();
    }

    @Override
    public int retrait() {
        try {
            placesOccupees.acquire();
        }
        catch( InterruptedException e ) {}
        int val = super.retrait();
        placesLibres.release();
        return val;
    }
}

BoiteALettresAvecSemaphore.java (V2)

package gloo.synchronisation;

import java.util.concurrent.Semaphore;

public class BoiteALettresAvecSemaphore extends BoiteALettres {

    private Semaphore placesLibres;
    private Semaphore placesOccupees;
    // Deux sémaphores d'exclusion mutuelle pour protéger des dépôts et retraits simultanés
    // Une autre solution est que les deux méthodes de BoiteALettres soient synchronized
    private Semaphore protectionDepot = new Semaphore( 1 );
    private Semaphore protectionRetrait = new Semaphore( 1 );

    public BoiteALettresAvecSemaphore( int taille ) {
        super( taille );

        placesLibres = new Semaphore( taille );
        placesOccupees = new Semaphore( 0 );
    }

    @Override
    public void depot( int val ) {
        try {
            placesLibres.acquire();
            protectionDepot.acquire();
        }
        catch( InterruptedException e ) {}
        super.depot( val );
        protectionDepot.release();
        placesOccupees.release();
    }

    @Override
    public int retrait() {
        try {
            placesOccupees.acquire();
            protectionRetrait.acquire();
        }
        catch( InterruptedException e ) {}
        int val = super.retrait();
        protectionRetrait.release();
        placesLibres.release();
        return val;
    }
}

Information.java

package gloo.synchronisation.lecteurredacteur;

public class Information {

    private int nombreLecteurs = 0;
    private boolean redacteurPresent = false;

    public synchronized void accesLecture() {
        System.out.println( "Demande d'accès en lecture par " + Thread.currentThread().getName() );
        if( nombreLecteurs == 0 ) {
            try {
                while( redacteurPresent ) {
                    System.out.println( "Attente pour l'accès en lecture par " + Thread.currentThread().getName() );
                    wait();
                }
            }
            catch( InterruptedException e ) {}
        }
        ++nombreLecteurs;
        System.out.println( "Accès en lecture accordé à " + Thread.currentThread().getName() );
    }

    public synchronized void finLecture() {
        System.out.println( "Fin de l'accès en lecture par " + Thread.currentThread().getName() );
        --nombreLecteurs;
        if( nombreLecteurs == 0 ) notify();
    }

    public synchronized void accesEcriture() {
        System.out.println( "Demande d'accès en écriture par " + Thread.currentThread().getName() );
        try {
            while( redacteurPresent || nombreLecteurs > 0 ) {
                System.out.println( "Attente pour l'accès en écriture par " + Thread.currentThread().getName() );
                wait();
            }
        }
        catch( InterruptedException e ) {}
        redacteurPresent = true;
        System.out.println( "Accès en écriture accordé à " + Thread.currentThread().getName() );
    }

    public synchronized void finEcriture() {
        System.out.println( "Fin de l'accès en ecriture par " + Thread.currentThread().getName() );
        redacteurPresent = false;
        // Il est important de réveiller ici tous les lecteurs (pas seulement 1)
        notifyAll();
    }
}


© 2022-23 CentraleSupélec