Java - Moniteur réentrant

Bonjour à tous, futurs magiciens Java ! Aujourd'hui, nous allons entreprendre un voyage passionnant dans le monde des Moniteurs réentrants en Java. Ne vous inquiétez pas si vous êtes nouveaux en programmation - je serai votre guide amical, et nous avancerons pas à pas. Alors, sortez vos baguettes virtuelles (claviers), et plongeon !

Java - Reentrant Monitor

Qu'est-ce qu'un Moniteur réentrant ?

Avant de rentrer dans les détails, comprenons ce qu'est un Moniteur réentrant. Imaginez que vous êtes dans une bibliothèque magique où seulement une personne peut entrer dans une section spécifique à la fois. Et si vous êtes déjà dans cette section et que vous avez besoin d'aller plus profondément dans une sous-section ? Un Moniteur réentrant est comme un passe-magique qui vous permet de faire exactement cela - entrer dans une section dans laquelle vous êtes déjà !

En termes Java, un Moniteur réentrant permet à un thread qui détient déjà un verrou de l'acquérir à nouveau sans blocage. C'est comme vous donner la permission d'entrer dans une pièce dans laquelle vous êtes déjà. Génial, non ?

Pourquoi avons-nous besoin de Moniteurs réentrants ?

Vous vous demandez peut-être : "Pourquoi avons-nous besoin de ce passe-magique ?" Eh bien, dans le monde du multitâche (où plusieurs parties d'un programme s'exécutent simultanément), nous avons souvent besoin de protéger les ressources partagées. Les Moniteurs réentrants nous aident à le faire plus efficacement, surtout lorsque nous avons des méthodes appelant d'autres méthodes qui ont également besoin du même verrou.

Introduction à ReentrantLock

Java nous fournit une classe appelée ReentrantLock pour implémenter les Moniteurs réentrants. C'est comme notre passe-magique, mais sous forme de code !

Syntaxe

Voici comment nous créons et utilisons un ReentrantLock :

import java.util.concurrent.locks.ReentrantLock;

ReentrantLock lock = new ReentrantLock();

// Pour verrouiller
lock.lock();
try {
// Votre code protégé ici
} finally {
// Pour déverrouiller
lock.unlock();
}

Ne vous inquiétez pas si cela semble un peu intimidant. Nous le décomposerons avec des exemples !

Multithreading sans Reentrant Lock

Commençons par un exemple simple sans utiliser ReentrantLock. Imaginez que nous avons un compteur magique que plusieurs magiciens (threads) essaient d'incrémenter :

public class MagicalCounter {
private int count = 0;

public void increment() {
count++;
}

public int getCount() {
return count;
}
}

Maintenant, créons quelques threads de magiciens pour incrémenter ce compteur :

public class WizardThread extends Thread {
private MagicalCounter counter;

public WizardThread(MagicalCounter counter) {
this.counter = counter;
}

public void run() {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
}
}

public class MagicalCounterTest {
public static void main(String[] args) throws InterruptedException {
MagicalCounter counter = new MagicalCounter();
WizardThread wizard1 = new WizardThread(counter);
WizardThread wizard2 = new WizardThread(counter);

wizard1.start();
wizard2.start();

wizard1.join();
wizard2.join();

System.out.println("Compte final : " + counter.getCount());
}
}

Si vous exécutez cela, vous pourriez attendre que le compte final soit 2000 (1000 incrémentations de chaque magicien). Mais surprise ! Le résultat est souvent inférieur à 2000. Cela est dû au fait que nos magiciens se piétinent les pieds - ils essaient d'incrémenter le compteur en même temps, entraînant des incrémentations perdues.

Multithreading avec Reentrant Lock

Maintenant, ajoutons un peu de magie ReentrantLock à notre compteur :

import java.util.concurrent.locks.ReentrantLock;

public class MagicalCounterWithLock {
private int count = 0;
private ReentrantLock lock = new ReentrantLock();

public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}

public int getCount() {
return count;
}
}

Décomposons cela :

  1. Nous créons un objet ReentrantLock appelé lock.
  2. Dans la méthode increment, nous appelons lock.lock() avant d'incrémenter le compteur.
  3. Nous utilisons un bloc try-finally pour nous assurer que nous déverrouillons toujours, même en cas d'exception.
  4. Après l'incrément, nous appelons lock.unlock() dans le bloc finally.

Maintenant, si nous exécutons notre test WizardThread avec ce nouveau MagicalCounterWithLock, nous obtiendrons toujours 2000 comme compte final. Nos magiciens font maintenant la queue tranquillement !

Multithreading avec Reentrant Lock en mode True

ReentrantLock a un autre tour dans son sac. Nous pouvons le créer avec un paramètre d'équité :

ReentrantLock fairLock = new ReentrantLock(true);

Lorsque nous définissons l'équité sur true, le verrou favorise l'accès à la thread qui attend depuis le plus longtemps. C'est comme former une queue correcte pour nos magiciens !

Voici comment nous pourrions l'utiliser :

public class FairMagicalCounter {
private int count = 0;
private ReentrantLock fairLock = new ReentrantLock(true);

public void increment() {
fairLock.lock();
try {
count++;
} finally {
fairLock.unlock();
}
}

public int getCount() {
return count;
}
}

Cela garantit que si plusieurs magiciens attendent pour incrémenter le compteur, celui qui a attendu le plus longtemps passe en premier.

Conclusion

Et voilà, jeunes magiciens ! Nous avons parcouru le monde magique des Moniteurs réentrants en Java. Nous avons vu comment ils nous aident à gérer les ressources partagées dans les environnements multitâches, assurant que nos compteurs magiques (et autres objets partagés) sont incrémentés correctement.

Souvenez-vous, comme toute magie puissante, les Moniteurs réentrants devraient être utilisés avec sagesse. Ils sont excellents pour gérer l'accès concurrent aux ressources partagées, mais une utilisation excessive peut entraîner une baisse des performances ou même des deadlocks (une situation où les magiciens sont bloqués en attendant mutuellement leurs verrous pour toujours).

Pratiquez ces sorts... err, exemples de code, et bientôt vous lancerez des sorts de multitâche comme un pro ! Bonne programmation, et puissent vos threads toujours être en harmonie !

Credits: Image by storyset