Marco de concurrencia manuscrito de Java (1) 7 formas de implementar consultas asincrónicas para la sincronización

Prefacio

En esta sección, aprenderemos cómo implementar la conversión de consulta asíncrona a sincronización.Se presenta un total de 7 métodos de implementación comunes.

El mapa mental es el siguiente:

mapas mentales

Asíncrono a síncrono

Necesidades del negocio

Algunos resultados de comentarios de consultas de la interfaz se devuelven de forma asincrónica y los resultados de las consultas no se pueden obtener de inmediato.

Por ejemplo, durante el desarrollo empresarial, llamamos a otros sistemas, pero los resultados sí se notifican.

O en la implementación de rpc, el cliente llama al servidor y el resultado se devuelve de forma asíncrona, entonces, ¿cómo obtener el resultado de la llamada de forma síncrona?

  • Lógica de procesamiento normal

Active una operación asincrónica y luego pase un identificador único.

Espere hasta que se devuelva el resultado asincrónico y haga coincidir el resultado de acuerdo con el identificador único pasado.

  • Cómo convertir para sincronizar

Hay muchos escenarios de aplicaciones normales, pero a veces no desea almacenar datos, solo desea obtener el resultado de la llamada.

¿Qué debo hacer si quiero lograr el resultado de la operación de sincronización?

Ideas

  1. Iniciar una operación asincrónica

  2. Espere hasta que se devuelva el resultado asincrónico (se puede configurar el tiempo de espera)

  3. Una vez que se devuelve el resultado, el resultado de la operación asincrónica se devuelve uniformemente

Implementación común

  • Bucle esperando

  • esperar y notificar

  • Usar bloqueo de condición

  • Utilice CountDownLatch

  • Usar CyclicBarrier

  • Futuro

  • Spring EventListener

Aprendamos juntos sobre estos métodos de implementación.

Bucle esperando

Descripción

La espera de bucle es la forma más fácil de lograrlo.

Llamamos a la otra parte para solicitar una solicitud y seguimos repitiendo hasta que no hay resultado.

Este resultado puede estar en la memoria o en una base de datos como redis cache o mysql.

Código

Definir clase padre abstracta

Para facilitar la unificación de las otras implementaciones a continuación, primero definimos una clase padre abstracta.

/**
 * 抽象查询父类
 * @author binbin.hou
 * @since 1.0.0
 */
public abstract class AbstractQuery {

    private static final Log log = LogFactory.getLog(AbstractQuery.class);

    protected String result;

    public void asyncToSync() {
        startQuery();
        new Thread(new Runnable() {
            public void run() {
                remoteCall();
            }
        }).start();
        endQuery();
    }

    protected void startQuery() {
        log.info("开始查询...");
    }

    /**
     * 远程调用
     */
    protected void remoteCall() {
        try {
            log.info("远程调用开始");
            TimeUnit.SECONDS.sleep(5);
            result = "success";
            log.info("远程调用结束");
        } catch (InterruptedException e) {
            log.error("远程调用失败", e);
        }
    }

    /**
     * 查询结束
     */
    protected void endQuery() {
        log.info("完成查询,结果为:" + result);
    }

}

Código

La implementación sigue siendo muy simple, repitiendo hasta que no hay resultado.

TimeUnit.MILLISECONDS.sleep(10); En este caso, es más importante tomar una siesta en el bucle. Para evitar un aumento de la CPU, también se puede reducir a 1 ms, y puede ajustarlo de acuerdo con su propio negocio.

/**
 * 循环等待
 * @author binbin.hou
 * @since 1.0.0
 */
public class LoopQuery extends AbstractQuery {

    private static final Log log = LogFactory.getLog(LoopQuery.class);

    @Override
    protected void endQuery() {
        try {
            while (StringUtil.isEmpty(result)) {
                //循环等待一下
                TimeUnit.MILLISECONDS.sleep(10);
            }

            //获取结果

            log.info("完成查询,结果为:" + result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

prueba

LoopQuery loopQuery = new LoopQuery();
loopQuery.asyncToSync();
  • Iniciar sesión
[INFO] [2020-10-08 09:50:43.330] [main] [c.g.h.s.t.d.AbstractQuery.startQuery] - 开始查询...
[INFO] [2020-10-08 09:50:43.331] [Thread-0] [c.g.h.s.t.d.AbstractQuery.remoteCall] - 远程调用开始
[INFO] [2020-10-08 09:50:48.334] [Thread-0] [c.g.h.s.t.d.AbstractQuery.remoteCall] - 远程调用结束
[INFO] [2020-10-08 09:50:48.343] [main] [c.g.h.s.t.d.LoopQuery.endQuery] - 完成查询,结果为:success

Aquí puede ver que la llamada remota es Thread-0un hilo de ejecución, la llamada remota que consume mucho tiempo es 5S.

Característica de tiempo de espera

Por qué es necesario el tiempo de espera

Hay un problema con la implementación anterior, es decir, no hay tiempo de espera para el bucle.

Una de nuestras solicitudes de red puede fallar o la otra parte puede no manejarla correctamente después de recibir la solicitud.

Entonces, si seguimos esperando, es posible que nunca haya resultados, o los habrá después de mucho tiempo. Esto es insoportable en los negocios, por lo que es necesario agregar un período de tiempo de espera.

Código

/**
 * 循环等待-包含超时时间
 * @author binbin.hou
 * @since 1.0.0
 */
public class LoopTimeoutQuery extends AbstractQuery {

    private static final Log log = LogFactory.getLog(LoopTimeoutQuery.class);

    /**
     * 超时时间
     */
    private long timeoutMills = 3000;

    public LoopTimeoutQuery() {
    }

    public LoopTimeoutQuery(long timeoutMills) {
        this.timeoutMills = timeoutMills;
    }

    @Override
    protected void endQuery() {
        try {
            final long endTimeMills = System.currentTimeMillis() + timeoutMills;

            while (StringUtil.isEmpty(result)) {
                // 超时判断
                if(System.currentTimeMillis() >= endTimeMills) {
                    throw new RuntimeException("请求超时");
                }

                //循环等待一下
                TimeUnit.MILLISECONDS.sleep(10);
            }

            //获取结果

            log.info("完成查询,结果为:" + result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

prueba

LoopTimeoutQuery loopQuery = new LoopTimeoutQuery();
loopQuery.asyncToSync();

El registro es el siguiente:

[INFO] [2020-10-08 10:04:58.091] [main] [c.g.h.s.t.d.AbstractQuery.startQuery] - 开始查询...
[INFO] [2020-10-08 10:04:58.092] [Thread-0] [c.g.h.s.t.d.AbstractQuery.remoteCall] - 远程调用开始
Exception in thread "main" java.lang.RuntimeException: 请求超时
    at com.github.houbb.sync.test.demo.LoopTimeoutQuery.endQuery(LoopTimeoutQuery.java:38)
    at com.github.houbb.sync.test.demo.AbstractQuery.asyncToSync(AbstractQuery.java:26)
    at com.github.houbb.sync.test.demo.LoopTimeoutQuery.main(LoopTimeoutQuery.java:55)
[INFO] [2020-10-08 10:05:03.097] [Thread-0] [c.g.h.s.t.d.AbstractQuery.remoteCall] - 远程调用结束

El tiempo de espera se puede configurar y se puede configurar de acuerdo con su propio tiempo de respuesta durante el desarrollo normal.

Si la solicitud se agota, considere la solución correspondiente.

Basado en esperar () y notificar a todos ()

Introducción

De hecho, el ciclo consume más rendimiento. Para esta característica de espera, jdk encapsula una variedad de características para nosotros.

Por ejemplo, la combinación más común de esperar () para ingresar esperando y notificar a todos () para despertar esperando.

Esta es también la realización de colas de bloqueo. No introduciremos colas de bloqueo. Echemos un vistazo a la implementación de esperar + notificar.

implementación de java

package com.github.houbb.sync.test.demo;

import com.github.houbb.log.integration.core.Log;
import com.github.houbb.log.integration.core.LogFactory;

/**
 * wait+notify 实现
 * @author binbin.hou
 * @since 1.0.0
 */
public class WaitNotifyQuery extends AbstractQuery {

    private static final Log log = LogFactory.getLog(WaitNotifyQuery.class);

    /**
     * 声明对象
     */
    private final Object lock = new Object();

    @Override
    protected void remoteCall() {
        super.remoteCall();
        synchronized (lock) {
            log.info("远程线程执行完成,唤醒所有等待。");
            lock.notifyAll();
        }
    }

    @Override
    protected void endQuery() {
        try {
            // 等待 10s
            synchronized (lock) {
                log.info("主线程进入等待");
                lock.wait(10 * 1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        super.endQuery();
    }

    public static void main(String[] args) {
        WaitNotifyQuery query = new WaitNotifyQuery();
        query.asyncToSync();
    }

}

Nota: Al programar, debe usar sincronizado para garantizar la seguridad de la rosca del soporte del candado; de lo contrario, se informará un error.

prueba

El registro es el siguiente:

[INFO] [2020-10-08 11:05:50.769] [main] [c.g.h.s.t.d.AbstractQuery.startQuery] - 开始查询...
[INFO] [2020-10-08 11:05:50.770] [main] [c.g.h.s.t.d.WaitNotifyQuery.endQuery] - 主线程进入等待
[INFO] [2020-10-08 11:05:50.770] [Thread-0] [c.g.h.s.t.d.AbstractQuery.remoteCall] - 远程调用开始
[INFO] [2020-10-08 11:05:55.772] [Thread-0] [c.g.h.s.t.d.AbstractQuery.remoteCall] - 远程调用结束
[INFO] [2020-10-08 11:05:55.773] [Thread-0] [c.g.h.s.t.d.WaitNotifyQuery.remoteCall] - 远程线程执行完成,唤醒所有等待。
[INFO] [2020-10-08 11:05:55.773] [main] [c.g.h.s.t.d.AbstractQuery.endQuery] - 完成查询,结果为:success

Implementación de bloqueo condicional

Introducción a los bloqueos condicionales

Si desea escribir un objeto concurrente con múltiples predicados condicionales, o desea obtener más control que la visibilidad de la cola condicional, entonces las clases de implementación de bloqueo y condición explícitas proporcionan un bloqueo y condición mejor que los internos. Opciones más flexibles para las colas.

Así como Lock proporciona un conjunto de características que es mucho más rico que el bloqueo interno, Condition también proporciona un conjunto de características que es mucho más rico que las colas de condición internas:

Cada bloqueo puede tener varios conjuntos de espera (una colección de subprocesos suspendidos debido a la espera), esperas condicionales interrumpibles / ininterrumpidas, esperas basadas en el tiempo y una opción entre colas justas / injustas.

Introducción a la condición

Precauciones:

Los equivalentes de esperar, notificar y notificar a todos en el objeto Condición son espera, señal y señal a todos.

Sin embargo, Condition hereda de Object, lo que significa que también tiene métodos de espera y notificación.

¡Asegúrese de utilizar la versión correcta, espera y señal!

implementación de java

Para demostrar simplicidad, podemos seleccionar directamente la cerradura reentrante.

Una condición está asociada con un solo bloqueo, al igual que una cola de condición está asociada con un solo bloqueo interno;

Llame al método Lock.newCondition del bloqueo asociado con la condición para crear una condición.

package com.github.houbb.sync.test.demo;

import com.github.houbb.log.integration.core.Log;
import com.github.houbb.log.integration.core.LogFactory;

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

/**
 * 条件锁实现
 * @author binbin.hou
 * @since 1.0.0
 */
public class LockConditionQuery extends AbstractQuery {

    private static final Log log = LogFactory.getLog(LockConditionQuery.class);

    private final Lock lock = new ReentrantLock();

    private final Condition condition = lock.newCondition();

    @Override
    protected void remoteCall() {
        lock.lock();
        try{
            super.remoteCall();

            log.info("远程线程执行完成,唤醒所有等待线程。");
            condition.signalAll();
        } finally {
            lock.unlock();
        }

    }

    @Override
    protected void endQuery() {
        lock.lock();
        try{
            // 等待
            log.info("主线程进入等待");

            condition.await();

            super.endQuery();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        LockConditionQuery query = new LockConditionQuery();
        query.asyncToSync();
    }

}

La implementación también es relativamente simple, ingresamos el método, llamamos a lock.lock () para bloquear, finalmente llamamos a lock.unlock () para liberar el bloqueo.

condition.await();Entra esperando; condition.signalAll();despierta todos los hilos en espera.

Registro de prueba

[INFO] [2020-10-08 12:33:40.985] [main] [c.g.h.s.t.d.AbstractQuery.startQuery] - 开始查询...
[INFO] [2020-10-08 12:33:40.986] [main] [c.g.h.s.t.d.LockConditionQuery.endQuery] - 主线程进入等待
[INFO] [2020-10-08 12:33:40.987] [Thread-0] [c.g.h.s.t.d.AbstractQuery.remoteCall] - 远程调用开始
[INFO] [2020-10-08 12:33:45.990] [Thread-0] [c.g.h.s.t.d.AbstractQuery.remoteCall] - 远程调用结束
[INFO] [2020-10-08 12:33:45.991] [Thread-0] [c.g.h.s.t.d.LockConditionQuery.remoteCall] - 远程线程执行完成,唤醒所有等待线程。
[INFO] [2020-10-08 12:33:45.993] [main] [c.g.h.s.t.d.AbstractQuery.endQuery] - 完成查询,结果为:success

Implementación de pestillo CountDownLatch

CountDownLatch / Future / CyclicBarrier Estas tres son todas herramientas de sincronización proporcionadas por jdk. Aquí solo las presentamos brevemente.

Para obtener más detalles, consulte:

Herramientas de sincronización JCIP-19. Bloqueo / valla / semáforo / cola de bloqueo / FutureTask

Introducción a CountDownLatch

El bloqueo es una especie de herramienta de sincronización que puede retrasar el progreso de un hilo hasta que alcanza un estado de terminación.

La función de cerradura es equivalente a una puerta: hasta que la cerradura llega al estado final, la puerta se cierra y no puede pasar ningún hilo. Cuando se alcanza el estado final, la puerta se abre y deja pasar todos los hilos.

Cuando la cerradura llega al estado final, no cambiará el estado, por lo que la puerta siempre permanecerá abierta.

El bloqueo se puede utilizar para garantizar que ciertas actividades no continúen hasta que se completen todas las demás.

implementación de código java

import com.github.houbb.log.integration.core.Log;
import com.github.houbb.log.integration.core.LogFactory;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * CountDownLatch 实现
 * @author binbin.hou
 * @since 1.0.0
 */
public class CountDownLatchQuery extends AbstractQuery {

    private static final Log log = LogFactory.getLog(CountDownLatchQuery.class);

    /**
     * 闭锁
     * 调用1次,后续方法即可通行。
     */
    private final CountDownLatch countDownLatch = new CountDownLatch(1);

    @Override
    protected void remoteCall() {
        super.remoteCall();

        // 调用一次闭锁
        countDownLatch.countDown();
    }

    @Override
    protected void endQuery() {
        try {
//            countDownLatch.await();
            countDownLatch.await(10, TimeUnit.SECONDS);

            log.info("完成查询,结果为:" + result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        CountDownLatchQuery loopQuery = new CountDownLatchQuery();
        loopQuery.asyncToSync();
    }

}

Llamamos a los resultados antes de volver countDownLatch.await(10, TimeUnit.SECONDS);a esperar, donde puede especificar un tiempo de espera.

Después de que remoteCall () se complete de forma remota, ejecútelo countDownLatch.countDown();para que el programa continúe.

prueba

Código

CountDownLatchQuery loopQuery = new CountDownLatchQuery();
loopQuery.asyncToSync();

Iniciar sesión

[INFO] [2020-10-08 10:24:03.348] [main] [c.g.h.s.t.d.AbstractQuery.startQuery] - 开始查询...
[INFO] [2020-10-08 10:24:03.350] [Thread-0] [c.g.h.s.t.d.AbstractQuery.remoteCall] - 远程调用开始
[INFO] [2020-10-08 10:24:08.353] [Thread-0] [c.g.h.s.t.d.AbstractQuery.remoteCall] - 远程调用结束
[INFO] [2020-10-08 10:24:08.354] [main] [c.g.h.s.t.d.CountDownLatchQuery.endQuery] - 完成查询,结果为:success

La función de bloqueo proporcionada por jdk es muy conveniente.

Valla cíclica

Introducción

Una barrera es similar a un candado, puede bloquear un grupo de hilos hasta que ocurre un evento [CPJ 4.4.3]. La atresia es un objeto de una sola vez, una vez que entra en el estado final, no se puede restablecer.

La diferencia clave entre una valla y un candado es que todos los hilos deben alcanzar la posición de la valla al mismo tiempo para continuar la ejecución. El candado se usa para esperar eventos y el cercado se usa para esperar otros hilos.

implementación de java

package com.github.houbb.sync.test.demo;

import com.github.houbb.log.integration.core.Log;
import com.github.houbb.log.integration.core.LogFactory;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

/**
 * CyclicBarrier 实现
 * @author binbin.hou
 * @since 1.0.0
 */
public class CyclicBarrierQuery extends AbstractQuery {

    private static final Log log = LogFactory.getLog(CyclicBarrierQuery.class);

    private CyclicBarrier cyclicBarrier = new CyclicBarrier(2);

    @Override
    protected void remoteCall() {
        super.remoteCall();

        try {
            cyclicBarrier.await();
            log.info("远程调用进入等待");
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void endQuery() {
        try {
            cyclicBarrier.await();
            log.info("主线程进入等待");
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }

        super.endQuery();
    }

}

prueba

Código

public static void main(String[] args) {
    CyclicBarrierQuery cyclicBarrierQuery = new CyclicBarrierQuery();
    cyclicBarrierQuery.asyncToSync();
}

Iniciar sesión

[INFO] [2020-10-08 10:39:00.890] [main] [c.g.h.s.t.d.AbstractQuery.startQuery] - 开始查询...
[INFO] [2020-10-08 10:39:00.892] [Thread-0] [c.g.h.s.t.d.AbstractQuery.remoteCall] - 远程调用开始
[INFO] [2020-10-08 10:39:05.894] [Thread-0] [c.g.h.s.t.d.AbstractQuery.remoteCall] - 远程调用结束
[INFO] [2020-10-08 10:39:05.895] [Thread-0] [c.g.h.s.t.d.CyclicBarrierQuery.remoteCall] - 远程调用进入等待
[INFO] [2020-10-08 10:39:05.895] [main] [c.g.h.s.t.d.CyclicBarrierQuery.endQuery] - 主线程进入等待
[INFO] [2020-10-08 10:39:05.896] [main] [c.g.h.s.t.d.AbstractQuery.endQuery] - 完成查询,结果为:success

Se puede ver el hilo remoto Thread-0después de que la ejecución entre en espera, esta vez el hilo principal llama, luego también entró en espera.

Cuando el subproceso principal endQuery espera, se da por satisfecho que los dos subprocesos esperan al mismo tiempo y luego finaliza la ejecución.

Basado en Future

Introducción al futuro

El modelo Future se puede describir de la siguiente manera: tengo una tarea y la envío a Future, y Future completa la tarea por mí. Durante este período, puedo hacer lo que quiera por mí mismo. Después de un tiempo, puedo recuperar los resultados de Future. Equivale a realizar un pedido, después de un período de tiempo, puede tomar el pedido para recoger la mercancía y puede hacer cualquier otra cosa durante este período. La interfaz Future es el formulario de pedido, y la clase Executor realmente procesa el pedido, que produce productos de acuerdo con los requisitos de la interfaz Future.

La interfaz Future proporciona métodos para detectar si la tarea se ha ejecutado, esperar hasta que se ejecute la tarea para obtener el resultado y también establecer el tiempo de espera para la ejecución de la tarea. Este método de establecer el tiempo de espera es la clave para lograr el tiempo de espera de la ejecución del programa Java.

Introducción detallada:

JCIP-26-Executor Future FutureTask

implementación de código java

Usar Future para regresar es bastante diferente de la implementación anterior, por lo que podemos anular directamente el método anterior.

import com.github.houbb.log.integration.core.Log;
import com.github.houbb.log.integration.core.LogFactory;

import java.util.concurrent.*;

/**
 * Future 实现
 * @author binbin.hou
 * @since 1.0.0
 */
public class FutureQuery extends AbstractQuery {

    private static final Log log = LogFactory.getLog(FutureQuery.class);

    private final ExecutorService executorService = Executors.newSingleThreadExecutor();

    @Override
    public void asyncToSync() {
        //1. 开始调用
        super.startQuery();

        //2. 远程调用
        Future<String> stringFuture = remoteCallFuture();

        //3. 完成结果
        try {
            String result = stringFuture.get(10, TimeUnit.SECONDS);
            log.info("调用结果:{}", result);
        } catch (InterruptedException | TimeoutException | ExecutionException e) {
            e.printStackTrace();
        }
    }

    /**
     * 远程调用
     * @return Future 信息
     */
    private Future<String> remoteCallFuture() {
        FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                log.info("开始异步调用");
                TimeUnit.SECONDS.sleep(5);
                log.info("完成异步调用");
                return "success";
            }
        });

        executorService.submit(futureTask);
        // 关闭线程池
        executorService.shutdown();
        return futureTask;
    }

    public static void main(String[] args) {
        FutureQuery query = new FutureQuery();
        query.asyncToSync();
    }

}

Cuando se ejecuta la llamada remota, es una FutureTask, que luego se envía al grupo de subprocesos para su ejecución.

Al obtener los resultados, stringFuture.get(10, TimeUnit.SECONDS)puede especificar el período de tiempo de espera para obtener los resultados .

Iniciar sesión

El registro de prueba es el siguiente:

[INFO] [2020-10-08 12:52:05.175] [main] [c.g.h.s.t.d.AbstractQuery.startQuery] - 开始查询...
[INFO] [2020-10-08 12:52:05.177] [pool-1-thread-1] [c.g.h.s.t.d.FutureQuery.call] - 开始异步调用
[INFO] [2020-10-08 12:52:10.181] [pool-1-thread-1] [c.g.h.s.t.d.FutureQuery.call] - 完成异步调用
[INFO] [2020-10-08 12:52:10.185] [main] [c.g.h.s.t.d.FutureQuery.asyncToSync] - 调用结果:success

Spring EventListener

modo de escucha de eventos de primavera

El uso del modo de observador es muy adecuado para la llamada de resultado de una cosa completada.

Spring nos proporciona un mecanismo de monitoreo relativamente poderoso. Aquí mostramos un ejemplo de cómo usar Spring en combinación.

PD: Este ejemplo es un ejemplo que escribí hace 2 años. Para la integridad de toda la serie, lo moví aquí directamente como un suplemento.

Código

  • BookingCreatedEvent.java

Defina un objeto que transfiera atributos.

public class BookingCreatedEvent extends ApplicationEvent {

    private static final long serialVersionUID = -1387078212317348344L;

    private String info;

    public BookingCreatedEvent(Object source) {
        super(source);
    }

    public BookingCreatedEvent(Object source, String info) {
        super(source);
        this.info = info;
    }

    public String getInfo() {
        return info;
    }
}
  • BookingService.java

Descripción: Cuando se this.context.publishEvent(bookingCreatedEvent);active,
se @EventListenerespecificará el método para escuchar.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class BookingService {

    @Autowired
    private ApplicationContext context;

    private volatile BookingCreatedEvent bookingCreatedEvent;

    /**
     * 异步转同步查询
     * @param info
     * @return
     */
    public String asyncQuery(final String info) {
        query(info);

        new Thread(new Runnable() {
            @Override
            public void run() {
                remoteCallback(info);
            }
        }).start();

        while(bookingCreatedEvent == null) {
            //.. 空循环
            // 短暂等待。
            try {
                TimeUnit.MILLISECONDS.sleep(1);
            } catch (InterruptedException e) {
                //...
            }
            //2. 使用两个单独的 event...

        }

        final String result = bookingCreatedEvent.getInfo();
        bookingCreatedEvent = null;
        return result;
    }

    @EventListener
    public void onApplicationEvent(BookingCreatedEvent bookingCreatedEvent) {
        System.out.println("监听到远程的信息: " + bookingCreatedEvent.getInfo());
        this.bookingCreatedEvent = bookingCreatedEvent;
        System.out.println("监听到远程消息后: " + this.bookingCreatedEvent.getInfo());
    }

    /**
     * 执行查询
     * @param info
     */
    public void query(final String info) {
        System.out.println("开始查询: " + info);
    }

    /**
     * 远程回调
     * @param info
     */
    public void remoteCallback(final String info) {
        System.out.println("远程回调开始: " + info);

        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 重发结果事件
        String result = info + "-result";
        BookingCreatedEvent bookingCreatedEvent = new BookingCreatedEvent(this, result);
        //触发event
        this.context.publishEvent(bookingCreatedEvent);
    }
}
  • método de prueba
@RunWith(SpringJUnit4Cla***unner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class BookServiceTest {

    @Autowired
    private BookingService bookingService;

    @Test
    public void asyncQueryTest() {
        bookingService.asyncQuery("1234");
    }

}
  • Iniciar sesión
2018-08-10 18:27:05.958  INFO  [main] com.github.houbb.spring.lean.core.ioc.event.BookingService:84 - 开始查询:1234
2018-08-10 18:27:05.959  INFO  [Thread-2] com.github.houbb.spring.lean.core.ioc.event.BookingService:93 - 远程回调开始:1234
接收到信息: 1234-result
2018-08-10 18:27:07.964  INFO  [Thread-2] com.github.houbb.spring.lean.core.ioc.event.BookingService:73 - 监听到远程的信息: 1234-result
2018-08-10 18:27:07.964  INFO  [Thread-2] com.github.houbb.spring.lean.core.ioc.event.BookingService:75 - 监听到远程消息后: 1234-result
2018-08-10 18:27:07.964  INFO  [Thread-2] com.github.houbb.spring.lean.core.ioc.event.BookingService:106 - 已经触发event
2018-08-10 18:27:07.964  INFO  [main] com.github.houbb.spring.lean.core.ioc.event.BookingService:67 - 查询结果: 1234-result
2018-08-10 18:27:07.968  INFO  [Thread-1] org.springframework.context.support.GenericApplicationContext:993 - Closing org.springframework.context.support.GenericApplicationContext@5cee5251: startup date [Fri Aug 10 18:27:05 CST 2018]; root of context hierarchy

resumen

Este artículo presenta un total de 7 formas de convertir de asíncrono a síncrono, de hecho, la idea es la misma.

Espere antes de que finalice la ejecución asincrónica, despierte y espere después de que finalice la ejecución.

Por supuesto, además de resumir los métodos anteriores al escribir este artículo, también quiero proporcionar una base para escribir una herramienta asíncrona a síncrona más adelante.

En la siguiente sección, aprenderemos cómo encapsular esta función en un marco de conversión sincrónica, y aquellos que estén interesados ​​pueden prestarle atención para recibir el contenido más reciente en tiempo real.

Si cree que este artículo es útil para usted, por favor, comente, marque como favorito y reenvíe una ola. Tu aliento es mi mayor motivación ~

No sé lo que has ganado O si tiene más ideas, bienvenido a discutir conmigo en el área de mensajes y esperamos conocer sus pensamientos.

Dirección de código

Para facilitar el aprendizaje, todos los ejemplos del artículo han sido de código abierto:

Implementación 1-6: sincronización

lazo

cuenta regresiva

oyente-evento-primavera

Supongo que te gusta

Origin blog.51cto.com/9250070/2540875
Recomendado
Clasificación