15. Bloqueo y condición (a continuación): ¿Cómo utiliza Dubbo el proceso de tubería para lograr la sincronización de transferencia asincrónica? -Herramientas de concurrencia


Condición implementa las variables de condición en el modelo de gestión.

Solo hay una variable de condición en el programa Java incorporado, y el programa de control implementado por Lock & Condition admite múltiples variables de condición, que es una diferencia importante entre las dos.

1. ¿Cómo usar dos variables de condición para implementar rápidamente una cola de bloqueo?

Una cola de bloqueo requiere dos variables de condición, una es que la cola no está vacía (la cola vacía no puede salir de la cola) y la otra es que la cola no está llena (la cola está llena y la cola no está permitida).

public class BlockedQueue<T> {
	final Lock lock = new ReentrantLock();
	// 条件变量:队列不满
	final Condition notFull = lock.newCondition();
	// 条件变量:队列不空
	final Condition notEmpty = lock.newCondition();

	// 入队
	void enq(T x) {
		lock.lock();
		try {
			while (队列已满) {
				// 等待队列不满
				notFull.await();
			}
			// 省略入队操作...
			// 入队后, 通知可出队
			notEmpty.signal();
		} finally {
			lock.unlock();
		}
	}

	// 出队
	void deq() {
		lock.lock();
		try {
			while (队列已空) {
				// 等待队列不空
				notEmpty.await();
			}
			// 省略出队操作...
			// 出队后,通知可入队
			notFull.signal();
		} finally {
			lock.unlock();
		}
	}
}

Los procedimientos implementados de bloqueo y condición, la espera de subprocesos y la notificación solo pueden llamar a wait (), signal (), signalAll (), su semántica y wait (), notify (), notifyAll () utilizados por sincronizado para lograr el proceso de gestión. Del mismo modo, los métodos utilizados para implementar los dos procedimientos no pueden confundirse.

2. Sincrónico y asincrónico

En términos simples, es si la persona que llama necesita esperar el resultado, si es necesario, es síncrono; de lo contrario, es asíncrono.

Dos métodos para lograr asíncrono:

  • La persona que llama crea un subproceso y ejecuta la llamada al método en el subproceso, que se llama llamada asincrónica;
  • Cuando se implementa el método, se crea un nuevo subproceso para ejecutar la lógica principal, y el subproceso principal regresa directamente, denominado método asincrónico.

3. Análisis del código fuente de Dubbo

El protocolo TCP en sí es asíncrono. En el nivel del protocolo TCP, después de enviar una solicitud RPC, el subproceso no esperará el resultado de respuesta de la RPC.

Dubbo hizo algo asíncrono a sincrónico.
Para la siguiente llamada RPC simple, el método sayHello () es un método sincrónico por defecto, es decir, cuando se ejecuta service.sayHello ("dubbo"), el hilo se detendrá y esperará el resultado.

DemoService service = 初始化部分省略
String message = service.sayHello("dubbo");
System.out.println(message);

El subproceso de volcado salió y descubrió que el subproceso estaba bloqueado y el estado era TIMED_WAITING. Originalmente, la solicitud se envió de forma asincrónica, pero el subproceso de llamada se bloqueó, lo que indica que Dubbo hizo lo asíncrono a sincronizado. El hilo está bloqueado en el método DefaultFuture.get ().
Inserte la descripción de la imagen aquí
La línea 108 de DubboInvoker llama a DefaultFuture.get (). Esta línea primero llama al método de solicitud (inv, timeout). Este método en realidad está enviando una solicitud RPC y luego esperando el resultado de devolución RPC llamando al método get ().

public class DubboInvoker{
  Result doInvoke(Invocation inv){
    // 下面这行就是源码中 108 行
    // 为了便于展示,做了修改
    return currentClient 
      .request(inv, timeout)
      .get();
  }
}

El código relevante se simplifica, repitiendo nuestros requisitos: cuando el RPC devuelve el resultado, bloquea el hilo de llamada y deja que el hilo de llamada espere; cuando el RPC devuelve el resultado, despierta el hilo de llamada y deja que el hilo de llamada se ejecute nuevamente. Clásico mecanismo de notificación de espera. La siguiente es la implementación de Dubbo

// 创建锁与条件变量
	private final Lock lock = new ReentrantLock();
	private final Condition done = lock.newCondition();

	// 调用方通过该方法等待结果
	Object get(int timeout) {
		long start = System.nanoTime();
		lock.lock();
		try {
			while (!isDone()) {
				done.await(timeout);
				long cur = System.nanoTime();
				if (isDone() || cur - start > timeout) {
					break;
				}
			}
		} finally {
			lock.unlock();
		}
		if (!isDone()) {
			throw new TimeoutException();
		}
		return returnFromResponse();
	}

	// RPC 结果是否已经返回
	boolean isDone() {
		return response != null;
	}

	// RPC 结果返回时调用该方法
	private void doReceived(Response res) {
		lock.lock();
		try {
			response = res;
			if (done != null) {
				done.signal();
			}
		} finally {
			lock.unlock();
		}
	}

El subproceso de llamada espera a que el RPC devuelva el resultado llamando al método get (). En este método, todo lo que ve son "caras" conocidas: llame al bloqueo () para adquirir el bloqueo y finalmente desbloquee () para liberar el bloqueo; después de adquirir el bloqueo, La espera se logra llamando al método await () en un bucle clásico.

Cuando el resultado RPC regrese, llamará al método doReceived (). En este método, llame al bloqueo () para adquirir el bloqueo y finalmente llame al desbloqueo () para liberar el bloqueo. Después de adquirir el bloqueo, llame a la señal () para notificar al hilo de llamada que el resultado ha regresado , Ya no tienes que esperar.

97 artículos originales publicados · elogiados 3 · 10,000+ vistas

Supongo que te gusta

Origin blog.csdn.net/qq_39530821/article/details/102536337
Recomendado
Clasificación