Concurrencia Java - Interfaz de bloqueo

Una interfaz java.util.concurrent.locks.Lock se utiliza como mecanismo de sincronización de subprocesos similar a los bloques sincronizados. El nuevo mecanismo de bloqueo es más flexible y ofrece más opciones que un bloque sincronizado. Las principales diferencias entre un bloqueo y un bloque sincronizado son las siguientes:

  • Guarantee of sequence- El bloque sincronizado no ofrece ninguna garantía de secuencia en la que se dará acceso al hilo en espera. La interfaz de bloqueo lo maneja.

  • No timeout- El bloqueo sincronizado no tiene opción de tiempo de espera si no se otorga el bloqueo. La interfaz de bloqueo proporciona dicha opción.

  • Single method - El bloque sincronizado debe estar completamente contenido dentro de un solo método, mientras que los métodos de una interfaz de bloqueo lock () y unlock () se pueden llamar en diferentes métodos.

Métodos de bloqueo

A continuación se muestra la lista de métodos importantes disponibles en la clase Lock.

No Señor. Método y descripción
1

public void lock()

Adquiere la cerradura.

2

public void lockInterruptibly()

Adquiere el bloqueo a menos que se interrumpa el hilo actual.

3

public Condition newCondition()

Devuelve una nueva instancia de Condition que está vinculada a esta instancia de Lock.

4

public boolean tryLock()

Adquiere el candado solo si está libre en el momento de la invocación.

5

public boolean tryLock()

Adquiere el candado solo si está libre en el momento de la invocación.

6

public boolean tryLock(long time, TimeUnit unit)

Adquiere el candado si está libre dentro del tiempo de espera dado y no se ha interrumpido el hilo actual.

7

public void unlock()

Libera el candado.

Ejemplo

El siguiente programa TestThread demuestra algunos de estos métodos de la interfaz de bloqueo. Aquí usamos lock () para adquirir el bloqueo y desbloquear () para liberar el bloqueo.

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

class PrintDemo {
   private final Lock queueLock = new ReentrantLock();

   public void print() {
      queueLock.lock();

      try {
         Long duration = (long) (Math.random() * 10000);
         System.out.println(Thread.currentThread().getName() 
            + "  Time Taken " + (duration / 1000) + " seconds.");
         Thread.sleep(duration);
      } catch (InterruptedException e) {
         e.printStackTrace();
      } finally {
         System.out.printf(
            "%s printed the document successfully.\n", Thread.currentThread().getName());
         queueLock.unlock();
      }
   }
}

class ThreadDemo extends Thread {
   PrintDemo  printDemo;

   ThreadDemo(String name,  PrintDemo printDemo) {
      super(name);
      this.printDemo = printDemo;
   }   

   @Override
   public void run() {
      System.out.printf(
         "%s starts printing a document\n", Thread.currentThread().getName());
      printDemo.print();
   }
}

public class TestThread {

   public static void main(String args[]) {
      PrintDemo PD = new PrintDemo();

      ThreadDemo t1 = new ThreadDemo("Thread - 1 ", PD);
      ThreadDemo t2 = new ThreadDemo("Thread - 2 ", PD);
      ThreadDemo t3 = new ThreadDemo("Thread - 3 ", PD);
      ThreadDemo t4 = new ThreadDemo("Thread - 4 ", PD);

      t1.start();
      t2.start();
      t3.start();
      t4.start();
   }
}

Esto producirá el siguiente resultado.

Salida

Thread - 1  starts printing a document
Thread - 4  starts printing a document
Thread - 3  starts printing a document
Thread - 2  starts printing a document
Thread - 1   Time Taken 4 seconds.
Thread - 1  printed the document successfully.
Thread - 4   Time Taken 3 seconds.
Thread - 4  printed the document successfully.
Thread - 3   Time Taken 5 seconds.
Thread - 3  printed the document successfully.
Thread - 2   Time Taken 4 seconds.
Thread - 2  printed the document successfully.

Aquí usamos la clase ReentrantLock como implementación de la interfaz Lock. La clase ReentrantLock permite que un hilo bloquee un método incluso si ya tiene el bloqueo en otro método.