Uso de LockSupport y condición de los componentes del paquete concurrente de Java JUC

En este artículo, solo presentamos parte de la API y algunos conceptos de LockSupport y Condition. No hay ejemplos involucrados. Si quieres un ejemplo, puedes consultar la parte de cola de bloqueo del código fuente del JDK. También escribí un artículo sobre el bloqueo de colas.

En el artículo sobre el estado y ciclo de vida del hilo de Java , cuando presentamos el ciclo de vida de Java , presentamos que al llamar a LockSupport, el hilo estará en el estado WAITiNG y volverá al estado RUNNABLE. LockSupport define un conjunto de métodos estáticos públicos, estos métodos proporcionan las funciones de activación y bloqueo de subprocesos más básicas, es la herramienta básica para construir componentes de sincronización. LockSupport define un conjunto de métodos que comienzan con park para bloquear el hilo actual, y unpark (Thread thread) método para activar un hilo bloqueado. Los métodos API proporcionados por LockSupport son los siguientes:

Método API

descripción

parque vacío ()

Bloquear el hilo actual, llamar void unPack (hilo del hilo) o interrupción del hilo antes de regresar

void parkNanos (nanos largos)

Sobre la base de void park (), se agrega el tiempo de bloqueo y se devolverá el tiempo de espera. El tiempo de bloqueo máximo es nanos segundos

void parkUntil (fecha límite larga)

Sobre la base de void park (), se agrega un tiempo de bloqueo y se devolverá el tiempo de espera.El tiempo máximo de bloqueo no excede la fecha límite.

void park (bloqueador de objetos)

Estos tres métodos son los mismos que los anteriores, la única diferencia es que el bloqueador se agrega para identificar el objeto (objeto de bloqueo) que el hilo actual está esperando.

void parkNanos (bloqueador de objetos, nanos largos)

void parkUntil (Bloqueador de objetos, plazo largo)

void unPack (hilo de rosca)

Despertar hilos en estado de espera

 El uso de LockSupport es relativamente simple. Comparado con el uso de LockSupport, usamos más la interfaz de condición. Al introducir sincronizado, ya sabemos que sincronizado combina esperar (), esperar (tiempo de espera prolongado), notificar () y notificar a todos ( El método puede realizar el mecanismo de notificación de espera, y su realización la realiza principalmente el monitor del objeto. La misma Condición también implementa el mecanismo de notificación de espera, pero se implementa mediante una cola de espera, y el contenido de AQS se usa aquí. La siguiente es la comparación entre la implementación de interfaz sincronizada y de condición:

 

Al usar la interfaz Condition, debe obtenerse a través del método newCondition de Lock. Más adelante explicaremos el método newCondition de Lock. Aquí primero presentamos los métodos proporcionados por la interfaz Condition. La siguiente tabla enumera los métodos proporcionados por la interfaz Condition:

Método API

descripción

vacío espera ()

Ingrese al estado de espera hasta que sea notificado o interrumpido.

void awaitUninterruptbly ()

Entrar en estado de espera hasta que se le notifique, no sensible a las interrupciones

long awaitNanos (long nanosTimeout)

El hilo actual entra en estado de espera hasta que se le notifica. Interrupción o tiempo de espera, si devuelve 0 o un número negativo, significa tiempo de espera, de lo contrario devuelve el tiempo consumido.

espera booleana (tiempo largo, unidad TimeUnit)

Igual que awaitNanos (long nanosTimeout)

boolean awaitUntil (fecha límite)

El hilo actual entra en estado de espera hasta que se le notifica. Si se interrumpe o en un momento determinado, si no hay un punto de tiempo especificado, se le notificará y devolverá verdadero; de lo contrario, devolverá falso

señal nula ()

Despierta un hilo que espera en la Condición, el hilo debe obtener el bloqueo relacionado con la Condición antes de regresar del método de espera

señal nulaTodo ()

Despierta todos los hilos que esperan la condición, el hilo debe obtener el bloqueo relacionado con la condición antes de regresar del método de espera.

Decimos que al adquirir Condition, se debe adquirir un bloqueo. Usamos el método newCondition de ReentrantLock para ver cómo Lock adquiere la instancia de Condition. El código es el siguiente:

final ConditionObject newCondition() {
    return new ConditionObject();
}

El código anterior devuelve una instancia de ConditionObject. ConditionObject es una implementación de Condition, que es una clase interna de AQS. Cada objeto Condición contiene una cola (en lo sucesivo denominada cola de espera), que es la clave para la función de notificación / espera del objeto Condición. El siguiente análisis de la implementación de Condition en combinación con el código fuente incluye principalmente tres direcciones: cola de espera, espera y notificación.

En primer lugar, miramos la cola de espera de Condición. Aquellos que conocen AQS pueden tener una cierta comprensión de la cola de espera. Aunque Condición es una subclase de AQS, existen algunas diferencias entre la cola de espera de Condición y la cola de espera de AQS. La siguiente es la definición de ConditionObject en AQS:

public class ConditionObject implements Condition, java.io.Serializable {
    private static final long serialVersionUID = 1173984872572414699L;
    //队列中的第一个等待节点
    private transient Node firstWaiter;
    //队列中最后一个等待节点
    private transient Node lastWaiter;
    //构造方法
    public ConditionObject() { }
    ......
}

Una condición contiene una cola de espera y la condición tiene un primer nodo (firstWaiter) y un último nodo (lastWaiter). Para obtener la definición de nodo, consulte un blog de AQS: Sincronizador de cola AQS Principio Análisis: adquisición exclusiva del estado de sincronización . Cuando el hilo actual llama al método Condition.await (), el nodo se construirá con el hilo actual y el nodo se agregará a la cola de espera desde la cola. A continuación, se muestra el diagrama de estructura de la cola de espera de Condición:

Como se muestra en la figura, Condition tiene una referencia a los nodos de cabeza y cola, y agregar un nuevo nodo solo necesita apuntar el nodo de cola original nextWaiter y actualizar el nodo de cola. La diferencia con la cola de sincronización es que una instancia de bloqueo también es una instancia de AQS. Puede haber varias colas de espera de condición. La siguiente figura muestra la estructura de la cola de sincronización y la cola de condición de AQS:

Cuando realmente llamamos al método Condition comenzando con await, el hilo actual entrará en la cola de espera y liberará el bloqueo, y el estado del hilo cambiará al estado de espera. Tomamos await () como ejemplo para analizar el proceso de espera de Condition desde la perspectiva del código fuente. El código es el siguiente:

public final void await() throws InterruptedException {
    //从线程中断抛出异常
    if (Thread.interrupted())
        throw new InterruptedException();
    //将节点添加到等待队列
    Node node = addConditionWaiter();
    //释放锁
    int savedState = fullyRelease(node);
    int interruptMode = 0;
    //如果不是同步队列类型的节点,将线程改为WAITING状态,
    //下面添加的节点的状态为Condition类型,不是同步类型
    while (!isOnSyncQueue(node)) {
        LockSupport.park(this);
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                break;
    }
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    if (node.nextWaiter != null) // clean up if cancelled
        unlinkCancelledWaiters();
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode);
}
//将节点添加到等待队列
private Node addConditionWaiter() {
    Node t = lastWaiter;
    // If lastWaiter is cancelled, clean out.
    if (t != null && t.waitStatus != Node.CONDITION) {
        unlinkCancelledWaiters();
        t = lastWaiter;
    }
    //创建节点实例,类型为Condition
    Node node = new Node(Thread.currentThread(), Node.CONDITION);
    if (t == null)
        firstWaiter = node;
    else
        t.nextWaiter = node;
        lastWaiter = node;
        return node;
}

El subproceso que llamó a este método adquirió con éxito el subproceso de bloqueo, que es el primer nodo en la cola de sincronización. Este método construirá el subproceso actual en un nodo y lo agregará a la cola de espera, luego liberará el estado de sincronización, activará los nodos subsiguientes en la cola de sincronización y luego El hilo actual entrará en estado de espera. El primer nodo de la cola de sincronización no se une directamente a la cola de espera, pero construye el hilo actual en un nuevo nodo a través del método addConditionWaiter () y lo agrega a la cola de espera. La estructura de la cola es la siguiente:

Llamar al método signal () de Condition despertará el nodo que ha estado esperando durante más tiempo en la cola de espera (el primer nodo). Antes de despertar el nodo, el nodo se moverá a la cola de sincronización. Tomamos signal () como ejemplo como código fuente:

public final void signal() {
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    Node first = firstWaiter;
    if (first != null)
    doSignal(first);
}

private void doSignal(Node first) {
    do {
       if ( (firstWaiter = first.nextWaiter) == null)
          lastWaiter = null;
          first.nextWaiter = null;
        }while (!transferForSignal(first) &&(first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
    //如果不能改变节点状态,取消
    if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
        return false;

    //家人到同步队列
    Node p = enq(node);
    int ws = p.waitStatus;
    if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
        //唤醒线程
        LockSupport.unpark(node.thread);
        return true;
}

El método signal () realiza una comprobación isHeldExclusively () para comprobar si el hilo actual es el hilo que ha adquirido el bloqueo. Luego, obtenga el primer nodo de la cola de espera, muévalo a la cola de sincronización y use LockSupport para reactivar los subprocesos en el nodo. Al llamar al método enq (nodo de nodo) del sincronizador, el subproceso del nodo principal en la cola de espera se puede mover de forma segura a la cola de sincronización. Cuando el nodo se mueve a la cola de sincronización, el hilo actual usa LockSupport para activar el hilo del nodo. El diagrama de estructura del despertador es el siguiente:

Esta es la introducción al mecanismo de notificación en espera de LockSupport y Condition. Si desea utilizar el ejemplo, se recomienda leer el código fuente de la parte de la cola de bloqueo, ya que la parte fuente de la cola de bloqueo utiliza una gran cantidad de mecanismos de notificación de condición en espera y LockSupport. No enumeraré ningún ejemplo aquí. 

Supongo que te gusta

Origin blog.csdn.net/wk19920726/article/details/108508677
Recomendado
Clasificación