Uso simple del sincronizador de cola AQS AbstractQueuedSynchronizer

 Sincronizador de cola AbstractQueuedSynchronizer se utiliza para construir un marco básico para bloqueos u otros componentes de sincronización. Utiliza una variable miembro int para representar el estado de sincronización y completa la puesta en cola de los subprocesos de adquisición de recursos a través de la cola FIFO incorporada. Podemos mirar el código fuente de bloqueos reentrantes, bloqueos de lectura-escritura o semáforos, CountDownLatch y otras fuentes introducidas anteriormente. Encontrará que siempre hay una clase interna estática en su código fuente que hereda AbstractQueuedSynchronizer. El ejemplo de código es el siguiente:

static class Sync extends AbstractQueuedSynchronizer {
    //具体逻辑
}

El uso principal del sincronizador es la herencia. La subclase administra el estado de sincronización heredando el sincronizador e implementando su método abstracto. En AbstractQueuedSynchronizer, hay un estado variable que identifica el estado de sincronización y proporciona getState (), setState (int newState) y compareAndSetState (int espera, int actualización)) Tres métodos para operar el estado sincrónico. Pueden garantizar que el cambio de estado sea seguro.

El sincronizador está diseñado en función del patrón del método de plantilla, es decir, el usuario debe heredar el sincronizador y reescribir el método especificado, y luego combinar el sincronizador en la implementación del componente de sincronización personalizado y llamar al método de plantilla proporcionado por el sincronizador. Y estos métodos de plantilla llamarán al método anulado por el usuario. El siguiente es el método de plantilla proporcionado por el sincronizador:

Nombre del método

Descripción

adquirir vacío (int arg)

Adquisición exclusiva del estado de sincronización, si el hilo actual adquiere el estado con éxito, este método regresará, de lo contrario ingresará a la cola de sincronización para esperar, y este método llamará al método reescrito tryAcquire (iny arg)

anular adquirir de forma interrumpida (int arg)

Igual que adquirir (int arg), pero este método responderá a la interrupción. El hilo actual entra en la cola de sincronización sin adquirir el estado de sincronización. Si el hilo actual se interrumpe, se lanzará una InterruptedException.

 

boolean tryAcquireNanos (int arg, long nanos)

El límite de tiempo de espera se incrementa en base a la adquisición de interrupciones (int arg). Si el estado de sincronización no se obtiene dentro del tiempo especificado, devolverá falso, de lo contrario devolverá verdadero.

 

adquirirCompartir vacío (int arg)

La diferencia entre el acceso compartido al estado de sincronización y el acceso exclusivo es que varios subprocesos pueden obtener el estado de sincronización al mismo tiempo. Llamará al método de reescritura tryAcquireShared

anular adquirirShareInterruptiblemente (int arg)

Igual que adquirirShare (int arg), pero este método responderá a la interrupción

boolean tryAcquireSharedNanos (int arg, long nanos)

Se agrega un límite de tiempo de espera sobre la base de adquirirShareInterruptiblemente (int arg). Si el estado de sincronización no se obtiene dentro del tiempo especificado, devolverá falso, de lo contrario, devolverá verdadero.

lanzamiento booleano (int arg)

Liberar exclusivamente el estado de sincronización. Este método activará el hilo contenido en el primer nodo de la cola de sincronización después de liberar el estado de sincronización. Llamará al método de reescritura tryRelease

boolean releaseShared (int arg)

Estado de sincronización de la versión compartida, llamará al método de anulación tryReleaseShared

Colección <Thread> getQueueThreads ()

Obtenga la colección de hilos en la cola de espera

Como dijimos antes, se llamará al método de plantilla y anularemos el método anulado. Echemos un vistazo a los métodos proporcionados por el sincronizador que debemos anular. La siguiente tabla muestra los métodos que el sincronizador puede anular:

Nombre del método

Descripción

tryAcquire booleano protegido (int arg)

Obtenga el estado de sincronización exclusivamente. Para implementar este método, debe consultar el estado actual y determinar si el estado de sincronización cumple con las expectativas, y luego realizar CAS para establecer el estado de sincronización

tryRelease booleano protegido (int arg)

Liberar exclusivamente el estado de sincronización, los subprocesos que esperan obtener el estado de sincronización tendrán la oportunidad de obtener el estado de sincronización

protegido int tryAcquireShared (int arg)

Acceso compartido al estado de sincronización, se devuelve un valor mayor que 0 para indicar éxito; de lo contrario, la adquisición falla.

tryReleaseShared booleano protegido (int arg)

Estado de sincronización de la versión compartida.

protegido booleano isHeldExclusively ()

Si el sincronizador actual está ocupado por un hilo en modo exclusivo, generalmente este método indica si está ocupado exclusivamente por el hilo actual

Anteriormente presentamos el método de plantilla y el método regrabable del sincronizador. A partir del método, podemos ver que los métodos de plantilla proporcionados por el sincronizador se dividen básicamente en tres categorías: estado de sincronización de adquisición y liberación exclusiva, estado de sincronización de adquisición y liberación compartida y sincronización de consulta El estado de los subprocesos en espera en la cola. El componente de sincronización personalizado utilizará el método de plantilla proporcionado por el sincronizador para implementar su propia semántica de sincronización. A continuación, se utilizará un ejemplo de bloqueo exclusivo para obtener más información sobre el uso de sincronizadores. En primer lugar, nuestra última plantilla, similar a las herramientas proporcionadas por JUC, escribe un sincronizador de herencia de clases interno.

public class MutexLock{
    private final Sync sync = new Sync();
    //内部类继承同步器
    private static class Sync extends AbstractQueuedSynchronizer {
	private static final long serialVersionUID = 1L;
    }
    //获取锁
    public void lock() {
    }
    //可中断获取锁
    public void lockInterruptibly() throws InterruptedException {
    }
    //尝试获取锁
    public boolean tryLock() {
	return false;
    }
    //可超时尝试获取锁
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
	return false;
    }
    //释放锁
    public void unlock() {
    }
}

Como se muestra en el código anterior, es una plantilla escrita sin ninguna implementación, y a continuación necesitamos usar el método de plantilla proporcionado por el sincronizador y el método regrabable para lograr el bloqueo exclusivo anterior. Este es solo un ejemplo simple para aprender. Después de aprender el principio de realización del sincronizador, se escribirán herramientas de sincronización más complejas. Guiamos al sincronizador a través del método de plantilla anterior para proporcionar tres métodos para adquirir bloqueos y un método para liberar bloqueos. Podemos implementar el siguiente código lógico de la siguiente manera:

public class MutexLock{
    private final Sync sync = new Sync();
    private static class Sync extends AbstractQueuedSynchronizer {
	private static final long serialVersionUID = 1L;
    }
    public void lock() {
	//调用同步器的方法获取锁
	sync.acquire(1);
    }
    public void lockInterruptibly() throws InterruptedException {
	//调用同步器的方法可中断获取锁
	sync.acquireInterruptibly(1);
    }
    public boolean tryLock() {
	return false;
    }
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        //默认传入的是nanoseconds,不再做时间的转换
        return sync.tryAcquireNanos(1, time);
    }
    public void unlock() {
        //调用同步器的方法释放锁
        sync.release(1);
    }
}

Anteriormente dijimos que void adquirir (int arg) llamará al método reescrito tryAcquire (iny arg), por lo que necesitamos reescribir el método tryAcquire (iny arg) para cambiar el estado del sincronizador. Si tiene éxito, adquiere el bloqueo. Si falla, agréguelo a la cola de espera. La implementación del siguiente código.

private static class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = 1L;
    public boolean tryAcquire(int acquires) {
        //如果将状态改变为1,如果成功则获取到锁
	if (compareAndSetState(0, 1)) {
	    setExclusiveOwnerThread(Thread.currentThread());
	    return true;
	}
        return false;
    }
    //释放锁
    protected boolean tryRelease(int releases) {
        if (getState() == 0)
            throw new IllegalMonitorStateException();
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }
    }
}

Este blog es principalmente para explicar el uso básico del sincronizador AbstractQueuedSynchronizer y comprender los métodos de plantilla y los métodos regrabables proporcionados por AbstractQueuedSynchronizer. Podemos usar AbstractQueuedSynchronizer para construir rápidamente un componente de sincronización. Analizaremos el principio de implementación de AbstractQueuedSynchronizer en detalle más adelante.

Supongo que te gusta

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