Voorbeeld van Java Lock – ReentrantLock

Welkom bij het Java Lock voorbeeld tutorial. Gewoonlijk gebruiken we bij het werken in een multi-threaded omgeving gesynchroniseerd voor threadveiligheid.

Java Lock

Meestal is het sleutelwoord `synchronized` de juiste keuze, maar het heeft enkele tekortkomingen die hebben geleid tot de introductie van de Lock API in de Java Concurrency package. De Java 1.5 Concurrency API kwam met het java.util.concurrent.locks pakket met de Lock interface en enkele implementatieklassen om het objectvergrendelingsmechanisme te verbeteren. Enkele belangrijke interfaces en klassen in de Java Lock API zijn:

  1. Lock: Dit is de basisinterface voor de Lock API. Het biedt alle functies van het sleutelwoord `synchronized`, met aanvullende manieren om verschillende voorwaarden voor vergrendeling te creëren, het instellen van een time-out voor de thread om op een vergrendeling te wachten. Enkele van de belangrijke methoden zijn lock() om de vergrendeling te verkrijgen, unlock() om de vergrendeling vrij te geven, tryLock() om te wachten op de vergrendeling gedurende een bepaalde periode, newCondition() om de voorwaarde te creëren, enz.

  2. Voorwaarde: Voorwaarde-objecten zijn vergelijkbaar met het Object wait-notify-model met extra functies om verschillende sets van wachtenden te creëren. Een Voorwaarde-object wordt altijd gemaakt door een Vergrendeling-object. Enkele van de belangrijke methoden zijn await(), wat vergelijkbaar is met wait(), en signal(), signalAll(), wat vergelijkbaar is met de notify() en notifyAll() methoden.

  3. LezenSchrijvenVergrendeling: Het bevat een paar bijbehorende vergrendelingen, een voor alleen-lezen bewerkingen en een andere voor schrijven. De leesvergrendeling kan tegelijkertijd worden vastgehouden door meerdere lezerthreads, zolang er geen schrijverthreads zijn. De schrijfvergrendeling is exclusief.

  4. ReentrantLock: Dit is de meest gebruikte implementatieklasse van het Lock-interface. Deze klasse implementeert het Lock-interface op een vergelijkbare manier als het synchronized-woord. Naast de implementatie van het Lock-interface bevat ReentrantLock enkele hulpprogramma-methoden om de thread die het slot vasthoudt te verkrijgen, threads die wachten om het slot te verkrijgen, enzovoort. Gesynchroniseerde blokken zijn reentrant van aard, dat wil zeggen als een thread het slot heeft van het bewakingsobject en als een ander gesynchroniseerd blok het slot op hetzelfde bewakingsobject nodig heeft, kan de thread dat codeblok betreden. Ik denk dat dit de reden is voor de naam van de klasse ReentrantLock. Laten we dit kenmerk begrijpen aan de hand van een eenvoudig voorbeeld.

    public class Test{
    
    public synchronized foo(){
        //do something
        bar();
      }
    
      public synchronized bar(){
        //do some more
      }
    }
    

    Als een thread foo() binnenkomt, heeft deze het slot op het Test-object, dus wanneer het probeert de methode bar() uit te voeren, mag de thread de methode bar() uitvoeren, aangezien deze al het slot op het Test-object heeft, dat wil zeggen hetzelfde als synchronized(this).

Java Lock Voorbeeld – ReentrantLock in Java

Laten we nu een eenvoudig voorbeeld bekijken waar we het synchronized-woord vervangen door de Java Lock API. Laten we zeggen dat we een Resource-klasse hebben met enkele bewerkingen waar we willen dat deze thread-safe is en enkele methoden waar threadveiligheid niet nodig is.

package com.journaldev.threads.lock;

public class Resource {

	public void doSomething(){
		//enkele bewerkingen uitvoeren, DB lezen, schrijven, enzovoort
	}
	
	public void doLogging(){
		//logging, geen noodzaak voor threadveiligheid
	}
}

Nu laten we zeggen dat we een Runnable-klasse hebben waar we Resource-methoden zullen gebruiken.

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();
	}
}

Merk op dat ik een gesynchroniseerd blok gebruik om het slot op het Resource-object te verkrijgen. We hadden een dummy-object in de klasse kunnen maken en dat voor het vergrendelingsdoel kunnen gebruiken. Laten we nu eens kijken hoe we de java Lock API kunnen gebruiken en het bovenstaande programma opnieuw kunnen schrijven zonder het gesynchroniseerde trefwoord te gebruiken. We zullen ReentrantLock in Java gebruiken.

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{
			// slot vrijgeven
			lock.unlock();
		}
		resource.doLogging();
	}

}

Zoals je kunt zien, gebruik ik de tryLock()-methode om ervoor te zorgen dat mijn thread alleen wacht voor een bepaalde tijd en als het het slot op het object niet krijgt, logt het gewoon en verlaat het. Een ander belangrijk punt om op te merken is het gebruik van het try-finally-blok om ervoor te zorgen dat het slot wordt vrijgegeven, zelfs als de methode doSomething() een uitzondering werpt.

Java Lock vs synchronized

Op basis van bovenstaande details en programma kunnen we gemakkelijk de volgende verschillen tussen Java Lock en synchronisatie concluderen.

  1. Java Lock API biedt meer zichtbaarheid en opties voor vergrendeling, in tegenstelling tot gesynchroniseerd waar een thread mogelijk oneindig lang moet wachten op het slot, kunnen we tryLock() gebruiken om ervoor te zorgen dat een thread slechts voor een bepaalde tijd wacht.
  2. Synchronisatiecode is veel schoner en gemakkelijker te onderhouden, terwijl bij Lock we gedwongen worden om een try-finally-blok te hebben om ervoor te zorgen dat Lock wordt vrijgegeven, zelfs als er een uitzondering wordt gegenereerd tussen lock() en unlock() methodeaanroepen.
  3. synchronized blokken of methoden kunnen slechts één methode omvatten, terwijl we de vergrendeling kunnen verkrijgen in één methode en deze in een andere methode kunnen vrijgeven met de Lock API.
  4. het sleutelwoord ‘synchronized’ biedt geen rechtvaardigheid, terwijl we rechtvaardigheid kunnen instellen op waar bij het maken van een ReentrantLock-object, zodat het langst wachtende draad als eerste de vergrendeling krijgt.
  5. We kunnen verschillende voorwaarden creëren voor Lock en verschillende threads kunnen wachten () op verschillende voorwaarden.

Dat is alles voor het Java Lock-voorbeeld, ReentrantLock in Java en een vergelijkende analyse met het sleutelwoord ‘synchronized’.

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