3.1 Objeto activo

Un objeto activo es un patrón de diseño que separa el hilo de ejecución de un método del hilo en el que se llamó. El propósito de este patrón es proporcionar una ejecución paralela mediante llamadas a métodos asincrónicos y un programador de procesamiento de solicitudes.

Versión simplificada:

objeto activo

Variante clásica:

Objeto activo 2

Esta plantilla tiene seis elementos:

  • Un objeto proxy que proporciona una interfaz para los métodos públicos del cliente.
  • Una interfaz que define métodos de acceso para el objeto activo.
  • Lista de solicitudes entrantes de clientes.
  • Un programador que determina el orden en que se ejecutarán las consultas.
  • Implementación de métodos de objetos activos.
  • Un procedimiento de devolución de llamada o una variable para que el cliente reciba un resultado.

3.2 bloqueo

El patrón de bloqueo es un mecanismo de sincronización que permite el acceso exclusivo a un recurso compartido entre varios subprocesos. Los bloqueos son una forma de hacer cumplir la política de control de concurrencia.

Básicamente, se utiliza un bloqueo suave, con la suposición de que cada subproceso intenta "adquirir el bloqueo" antes de acceder al recurso compartido correspondiente.

Sin embargo, algunos sistemas proporcionan un mecanismo de bloqueo obligatorio mediante el cual se anulará un intento de acceso no autorizado a un recurso bloqueado al generar una excepción en el subproceso que intentó obtener acceso.

Un semáforo es el tipo de cerradura más simple. En cuanto al acceso a los datos, no se distingue entre modos de acceso: compartido (solo lectura) o exclusivo (lectura-escritura). En el modo compartido, varios subprocesos pueden solicitar un bloqueo para acceder a los datos en modo de solo lectura. El modo de acceso exclusivo también se utiliza en los algoritmos de actualización y eliminación.

patrón de bloqueo

Los tipos de bloqueos se distinguen por la estrategia de bloquear la continuación de la ejecución del hilo. En la mayoría de las implementaciones, una solicitud de bloqueo evita que el subproceso continúe ejecutándose hasta que el recurso bloqueado esté disponible.

Un spinlock es un bloqueo que espera en un bucle hasta que se concede el acceso. Tal bloqueo es muy eficiente si un subproceso espera un bloqueo durante un período de tiempo pequeño, evitando así una reprogramación excesiva de subprocesos. El costo de esperar el acceso será significativo si uno de los subprocesos mantiene el bloqueo durante mucho tiempo.

patrón de bloqueo 2

Para implementar efectivamente el mecanismo de bloqueo, se requiere soporte a nivel de hardware. El soporte de hardware se puede implementar como una o más operaciones atómicas, como "probar y configurar", "buscar y agregar" o "comparar e intercambiar". Dichas instrucciones le permiten verificar sin interrupción que el candado está libre y, de ser así, adquirir el candado.

3.3 Supervisar

El patrón Monitor es un mecanismo de interacción y sincronización de procesos de alto nivel que proporciona acceso a recursos compartidos. Un enfoque para sincronizar dos o más tareas informáticas utilizando un recurso común, generalmente hardware o un conjunto de variables.

En la multitarea basada en monitor, el compilador o el intérprete inserta de forma transparente el código de bloqueo y desbloqueo en las rutinas formateadas apropiadamente, de forma transparente para el programador, evitando que el programador llame explícitamente a las primitivas de sincronización.

El monitor consta de:

  • un conjunto de procedimientos que interactúan con un recurso compartido
  • exclusión mutua
  • variables asociadas a este recurso
  • un invariante que define las condiciones para evitar una condición de carrera

El procedimiento de monitor adquiere el mutex antes de comenzar a trabajar y lo retiene hasta que finaliza el procedimiento o hasta que se espera una determinada condición. Si cada procedimiento garantiza que el invariante es verdadero antes de liberar el mutex, entonces ninguna tarea puede adquirir el recurso en una condición de carrera.

Así funciona el operador sincronizado en Java con los métodos wait()y notify().

3.4 Bloqueo de doble control

El bloqueo de doble verificación es un patrón de diseño paralelo destinado a reducir la sobrecarga de obtener un bloqueo.

En primer lugar, se comprueba la condición de bloqueo sin ninguna sincronización. Un subproceso intenta adquirir un bloqueo solo si el resultado de la comprobación indica que necesita adquirir el bloqueo.

//Double-Checked Locking
public final class Singleton {
private static Singleton instance; //Don't forget volatile modifier

public static Singleton getInstance() {
     if (instance == null) {                //Read

         synchronized (Singleton.class) {    //
             if (instance == null) {         //Read Write
                 instance = new Singleton(); //
             }
         }
     }
 }

¿Cómo crear un objeto singleton en un entorno seguro para subprocesos?

public static Singleton getInstance() {
   if (instance == null)
    instance = new Singleton();
}

Si crea un objeto Singleton a partir de diferentes subprocesos, puede haber una situación en la que se creen varios objetos al mismo tiempo, y esto es inaceptable. Por lo tanto, es razonable envolver la creación del objeto en una declaración sincronizada.

public static Singleton getInstance() {
    synchronized (Singleton.class) {
        if (instance == null)
        instance = new Singleton();
    }
}

Este enfoque funcionará, pero tiene un pequeño inconveniente. Después de crear el objeto, cada vez que intente obtenerlo en el futuro, se realizará una verificación en el bloque sincronizado, lo que significa que el hilo actual y todo lo relacionado con él se bloqueará. Así que este código se puede optimizar un poco:

public static Singleton getInstance() {
     if (instance != null)
        return instance;

    synchronized (Singleton.class) {
        if (instance == null)
        instance = new Singleton();
    }
}

En algunos lenguajes y/o en algunas máquinas no es posible implementar este patrón de forma segura. Por lo tanto, a veces se le llama antipatrón. Tales características han llevado a la aparición de la relación de orden estricto "sucede antes" en el modelo de memoria de Java y el modelo de memoria de C++.

Por lo general, se usa para reducir la sobrecarga de implementar la inicialización diferida en programas de subprocesos múltiples, como el patrón de diseño Singleton. En la inicialización diferida de una variable, la inicialización se difiere hasta que se necesita el valor de la variable en el cálculo.

3.5 Programador

El Programador es un patrón de diseño paralelo que proporciona un mecanismo para implementar una política de programación, pero es independiente de cualquier política en particular. Controla el orden en que los subprocesos deben ejecutar el código secuencial mediante un objeto que especifica explícitamente la secuencia de subprocesos en espera.