Exemple de verrouillage Java – ReentrantLock

Bienvenue dans le tutoriel sur l’exemple de verrou Java. Habituellement, lorsqu’on travaille dans un environnement multi-thread, nous utilisons la synchronisation pour la sécurité des threads.

Java Lock

La plupart du temps, le mot-clé synchronized est la meilleure option, mais il présente quelques lacunes qui ont conduit à l’inclusion de l’API Lock dans le package de concurrence Java. L’API de concurrence Java 1.5 est venue avec le package java.util.concurrent.locks avec l’interface Lock et quelques classes d’implémentation pour améliorer le mécanisme de verrouillage des objets. Certaines interfaces et classes importantes dans l’API Lock Java sont :

  1. Lock: Il s’agit de l’interface de base de l’API Lock. Elle fournit toutes les fonctionnalités du mot-clé synchronized avec des moyens supplémentaires pour créer différentes conditions de verrouillage, fournir un délai d’attente pour le thread en attente du verrou. Certaines des méthodes importantes sont lock() pour acquérir le verrou, unlock() pour libérer le verrou, tryLock() pour attendre le verrou pendant une certaine période de temps, newCondition() pour créer la Condition, etc.

  2. Condition: Les objets Condition sont similaires au modèle attente-notification d’objet avec une fonctionnalité supplémentaire pour créer différents ensembles d’attente. Un objet Condition est toujours créé par l’objet Verrou. Certaines des méthodes importantes sont attendre() qui est similaire à wait() et signal(), signalTout() qui est similaire à notify() et notifyTout() méthodes.

  3. VerrouDeLectureÉcriture: Il contient une paire de verrous associés, l’un pour les opérations en lecture seule et un autre pour l’écriture. Le verrou de lecture peut être détenu simultanément par plusieurs threads lecteurs tant qu’il n’y a pas de threads rédacteurs. Le verrou d’écriture est exclusif.

  4. ReentrantLock: C’est la classe d’implémentation la plus largement utilisée de l’interface Lock. Cette classe implémente l’interface Lock de la même manière que le mot-clé synchronized. En plus de l’implémentation de l’interface Lock, ReentrantLock contient quelques méthodes utilitaires pour obtenir le thread qui détient le verrou, les threads en attente pour acquérir le verrou, etc. Les blocs synchronisés sont réentrants, c’est-à-dire que si un thread a le verrou sur l’objet moniteur et qu’un autre bloc synchronisé nécessite également le verrou sur le même objet moniteur, alors le thread peut entrer dans ce bloc de code. Je pense que c’est la raison pour laquelle le nom de la classe est ReentrantLock. Comprenons cette fonctionnalité avec un exemple simple.

    public class Test{
    
    public synchronized foo(){
        //faire quelque chose
        bar();
      }
    
      public synchronized bar(){
        //faire un peu plus
      }
    }
    

    Si un thread entre dans foo(), il a le verrou sur l’objet Test, donc lorsqu’il essaie d’exécuter la méthode bar(), le thread est autorisé à exécuter la méthode bar() car il détient déjà le verrou sur l’objet Test, c’est-à-dire identique à synchronized(this).

Exemple de verrou Java – ReentrantLock en Java

Maintenant, voyons un exemple simple où nous remplacerons le mot-clé synchronized par l’API Java Lock. Supposons que nous ayons une classe Resource avec des opérations où nous voulons qu’elle soit thread-safe et des méthodes où la sécurité des threads n’est pas nécessaire.

package com.journaldev.threads.lock;

public class Resource {

	public void doSomething(){
		// faire quelques opérations, lecture, écriture DB, etc
	}
	
	public void doLogging(){
		// journalisation, pas besoin de sécurité des threads
	}
}

Maintenant, supposons que nous ayons une classe Runnable où nous utiliserons les méthodes de ressources.

package com.journaldev.threads.lock;

public class SynchronizedLockExample implements Runnable{

	private Resource resource;
	
	public SynchronizedLockExample(Resource r){
		this.resource = r;
	}
	
	@Override
	public void run() {
		synchronized (resource) {
			resource.doSomething();
		}
		resource.doLogging();
	}
}

Remarquez que j’utilise un bloc synchronisé pour acquérir le verrou sur l’objet Resource. Nous aurions pu créer un objet fictif dans la classe et l’utiliser à des fins de verrouillage. Voyons maintenant comment nous pouvons utiliser l’API Lock de Java et réécrire le programme ci-dessus sans utiliser le mot-clé synchronized. Nous utiliserons ReentrantLock en Java.

package com.journaldev.threads.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ConcurrencyLockExample implements Runnable{

	private Resource resource;
	private Lock lock;
	
	public ConcurrencyLockExample(Resource r){
		this.resource = r;
		this.lock = new ReentrantLock();
	}
	
	@Override
	public void run() {
		try {
			if(lock.tryLock(10, TimeUnit.SECONDS)){
			resource.doSomething();
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}finally{
			 // libérer le verrou 
			lock.unlock();
		}
		resource.doLogging();
	}

}

Comme vous pouvez le voir, j’utilise la méthode tryLock() pour m’assurer que mon thread n’attend que pour un temps défini et s’il ne parvient pas à obtenir le verrou sur l’objet, il se contente de consigner et de sortir. Un autre point important à noter est l’utilisation du bloc try-finally pour s’assurer que le verrou est libéré même si l’appel de la méthode doSomething() lance une exception.

Verrou Java vs synchronisé

Sur la base des détails et du programme ci-dessus, nous pouvons facilement conclure les différences suivantes entre Java Lock et la synchronisation.

  1. L’API Lock Java offre une visibilité et des options de verrouillage supplémentaires, contrairement à synchronized où un thread pourrait finir par attendre indéfiniment le verrou, nous pouvons utiliser tryLock() pour nous assurer que le thread attend seulement un temps spécifique.
  2. Le code de synchronisation est beaucoup plus propre et facile à maintenir, tandis qu’avec Lock, nous sommes obligés d’avoir un bloc try-finally pour nous assurer que le verrou est libéré même si une exception est lancée entre les appels des méthodes lock() et unlock().
  3. Les blocs ou méthodes de synchronisation ne peuvent couvrir qu’une seule méthode, alors que nous pouvons acquérir le verrou dans une méthode et le libérer dans une autre méthode avec l’API Lock.
  4. Le mot-clé synchronized ne garantit pas l’équité, alors que nous pouvons définir l’équité sur true lors de la création de l’objet ReentrantLock afin que le thread en attente depuis le plus longtemps obtienne le verrou en premier.
  5. Nous pouvons créer différentes conditions pour le verrou et différents threads peuvent attendre() pour différentes conditions.

C’est tout pour l’exemple de verrouillage en Java, ReentrantLock en Java et une analyse comparative avec le mot-clé synchronized.

Source:
https://www.digitalocean.com/community/tutorials/java-lock-example-reentrantlock