序言

本节将学习一下如何实现异步查询转同步的方式,共计介绍了 7 种常见的实现方式。

思维导图如下:

异步转同步

业务需求

有些接口查询反馈结果是异步返回的,无法立刻获取查询结果。

比如业务开发中我们调用其他系统,但是结果的返回确实通知的。

或者 rpc 实现中,client 调用 server 端,结果也是异步返回的,那么如何同步获取调用结果呢?

  • 正常处理逻辑

触发异步操作,然后传递一个唯一标识。

等到异步结果返回,根据传入的唯一标识,匹配此次结果。

  • 如何转换为同步

正常的应用场景很多,但是有时候不想做数据存储,只是想简单获取调用结果。

即想达到同步操作的结果,怎么办呢?

思路

  1. 发起异步操作

  2. 在异步结果返回之前,一直等待(可以设置超时)

  3. 结果返回之后,异步操作结果统一返回

常见的实现方式

  • 循环等待

  • wait & notify

  • 使用条件锁

  • 使用 CountDownLatch

  • 使用 CyclicBarrier

  • Future

  • Spring EventListener

下面我们一起来学习下这几种实现方式。

循环等待

说明

循环等待是最简单的一种实现思路。

我们调用对方一个请求,在没有结果之前一直循环查询即可。

这个结果可以在内存中,也可以放在 redis 缓存或者 mysql 等数据库中。

代码实现

定义抽象父类

为了便于后面的其他几种实现方式统一,我们首先定义一个抽象父类。

/**
* 抽象查询父类
* @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);
} }

代码实现

实现还是非常简单的,在没有结果之前一直循环。

TimeUnit.MILLISECONDS.sleep(10); 这里循环等待的小睡一会儿是比较重要的,避免 cpu 飙升,也可以降低为 1ms,根据自己的业务调整即可。

/**
* 循环等待
* @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();
}
} }

测试

LoopQuery loopQuery = new LoopQuery();
loopQuery.asyncToSync();
  • 日志
[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

这里可以看到远程调用是 Thread-0 线程执行的,远程调用的耗时为 5S。

超时特性

为什么需要超时时间

上面的实现存在一个问题,那就是循环等待没有超时时间。

我们的一个网络请求,可能存在失败,也可能对方收到请求之后没有正确处理。

所以如果我们一直等待,可能永远也没有结果,或者很久之后才有结果。这在业务上是不可忍受的,所以需要添加一个超时时间。

代码实现

/**
* 循环等待-包含超时时间
* @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();
}
} }

测试

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

日志如下:

[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] - 远程调用结束

超时时间是可以设定的,平时开发中可以根据自己的响应时间设置。

如果请求超时,考虑对应的兜底方案。

基于 wait() & notifyAll()

简介

实际上 loop 循环还是比较消耗性能的,对于这种等待特性, jdk 实际上为我们封装了多种特性。

比如最常见的 wait() 进入等待,notifyAll() 唤醒等待的组合方式。

这个同时也是阻塞队列的实现思想,阻塞队列我们就不介绍了,我们来看一下 wait+notify 的实现方式。

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();
} }

注意:编程时需要使用 synchronized 保证锁的持有者线程安全,不然会报错。

测试

日志如下:

[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

基于条件锁的实现

条件锁简介

如果你想编写一个含有多个条件谓词的并发对象,或者你想获得比条件队列的可见性之外更多的控制权,那么显式的Lock和Condition的实现类提供了一个比内部锁和条件队列更加灵活的选择。

如同Lock提供了比内部加锁要丰富得多的特征集一样,Condition也提供了比内部条件队列要丰富得多的特征集:

每个锁可以有多个等待集(因await挂起的线程的集合)、可中断/不可中断的条件等待、基于时限的等待以及公平/非公平队列之间的选择.

Condition 介绍

注意事项:

wait、notify和notifyAll在Condition对象中的对等体是await、signal和signalAll.

但是,Condition继承与Object,这意味着它也有wait和notify方法.

一定要确保使用了正确的版本–await和signal!

java 实现

为了演示简单,我们直接选择可重入锁即可。

一个Condition和一个单独的Lock相关联,就像条件队列和单独的内部锁相关联一样;

调用与Condition相关联的Lock的Lock.newCondition方法,可以创建一个Condition.

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();
} }

实现也比较简单,我们在方法进入,调用 lock.lock() 加锁,finally 中调用 lock.unlock() 释放锁。

condition.await(); 进入等待;condition.signalAll(); 唤醒所有等待线程。

测试日志

[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

CountDownLatch 闭锁实现

CountDownLatch/Future/CyclicBarrier 这三个都是 jdk 为我们提供的同步工具类,我们此处只做简单介绍。

详情参见:

JCIP-19-同步工具类。闭锁/栅栏/信号量/阻塞队列/FutureTask

CountDownLatch 简介

闭锁是一种同步工具类,可以延迟线程的进度直到其达到终止状态。

闭锁的作用相当于一扇门:在闭锁到达结束状态之前,这扇门一直是关闭的,并且没有任何线程能通过,当到达结束状态时,这扇门会打开并允许所有的线程通过。

当闭锁到达结束状态后,将不会再改变状态,因此这扇门将永远保持打开状态。

闭锁可以用来确保某些活动直到其它活动都完成后才继续执行。

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();
} }

我们在返回结果之前调用 countDownLatch.await(10, TimeUnit.SECONDS); 进行等待,这里可以指定超时时间。

remoteCall() 远程完成后,执行一下 countDownLatch.countDown();,进而可以让程序继续执行下去。

测试

代码

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

日志

[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

jdk 提供的闭锁功能还是非常的方便的。

CyclicBarrier 栅栏

简介

栅栏(Barrier)类似于闭锁,它能阻塞一组线程直到某个事件发生[CPJ 4.4.3]。闭锁是一次性对象,一旦进入最终状态,就不能被重置了。

栅栏与闭锁的关键区别在于,所有线程必须同时达到栅栏位置,才能继续执行。闭锁用于等待事件,而栅栏用于等待其他线程。

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();
} }

测试

代码

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

日志

[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

可以看出远程线程 Thread-0 执行完之后就进入等待,此时主线程调用,然后也进入等待。

等主线程 endQuery 等待时,就满足了两个线程同时等待,然后执行就结束了。

基于 Future 实现

Future 简介

Future模式可以这样来描述:我有一个任务,提交给了Future,Future替我完成这个任务。期间我自己可以去做任何想做的事情。一段时间之后,我就便可以从Future那儿取出结果。就相当于下了一张订货单,一段时间后可以拿着提订单来提货,这期间可以干别的任何事情。其中Future 接口就是订货单,真正处理订单的是Executor类,它根据Future接口的要求来生产产品。

Future接口提供方法来检测任务是否被执行完,等待任务执行完获得结果,也可以设置任务执行的超时时间。这个设置超时的方法就是实现Java程序执行超时的关键。

详细介绍:

JCIP-26-Executor Future FutureTask

java 代码实现

采用 Future 返回和以前的实现差异较大,我们直接覆写以前的方法即可。

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();
} }

远程调用执行时,是一个 FutureTask,然后提交到线程池去执行。

获取结果的时候,stringFuture.get(10, TimeUnit.SECONDS) 可以指定获取的超时时间。

日志

测试日志如下:

[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

spring 事件监听器模式

对于一件事情完成的结果调用,使用观察者模式是非常适合的。

spring 为我们提供了比较强大的监听机制,此处演示下结合 spring 使用的例子。

ps: 这个例子是2年前的自己写的例子了,此处为了整个系列的完整性,直接搬过来作为补充。

代码实现

  • BookingCreatedEvent.java

定义一个传输属性的对象。

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

说明:当 this.context.publishEvent(bookingCreatedEvent); 触发时,

会被 @EventListener 指定的方法监听到。

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);
}
}
  • 测试方法
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class BookServiceTest { @Autowired
private BookingService bookingService; @Test
public void asyncQueryTest() {
bookingService.asyncQuery("1234");
} }
  • 日志
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

小结

本文共计介绍了 7 种异步转同步的方式,实际上思想都是一样的。

在异步执行完成前等待,执行完成后唤醒等待即可。

当然我写本文除了总结以上几种方式以外,还想为后续写一个异步转同步的工具提供基础。

下一节我们将一起学习下如何将这个功能封装为一个同步转换框架,感兴趣的可以关注一下,便于实时接收最新内容。

觉得本文对你有帮助的话,欢迎点赞评论收藏转发一波。你的鼓励,是我最大的动力~

不知道你有哪些收获呢?或者有其他更多的想法,欢迎留言区和我一起讨论,期待与你的思考相遇。

代码地址

为了便于学习,文中的所有例子都已经开源:

实现 1-6:sync

loop

countdownlatch

spring-event-listener

java 手写并发框架(一)异步查询转同步的 7 种实现方式的更多相关文章

  1. 要想精通Mybatis?从手写Mybatis框架开始吧!

    1.Mybatis组成 动态SQL Config配置 Mapper配置 2.核心源码分析 Configuration源码解析 SqlSessionFactory源码解析 SqlSession源码解析 ...

  2. 重学 Java 设计模式:实战中介者模式「按照Mybaits原理手写ORM框架,给JDBC方式操作数据库增加中介者场景」

    作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 同龄人的差距是从什么时候拉开的 同样的幼儿园.同样的小学.一样 ...

  3. 手写DAO框架(三)-数据库连接

    -------前篇:手写DAO框架(二)-开发前的最后准备--------- 前言 上一篇主要是温习了一下基础知识,然后将整个项目按照模块进行了划分.因为是个人项目,一个人开发,本人采用了自底向上的开 ...

  4. 手写RPC框架指北另送贴心注释代码一套

    Angular8正式发布了,Java13再过几个月也要发布了,技术迭代这么快,框架的复杂度越来越大,但是原理是基本不变的.所以沉下心看清代码本质很重要,这次给大家带来的是手写RPC框架. 完整代码以及 ...

  5. 手写DAO框架(七)-如何保证连接可用

    版权声明:本文为博客园博主「水木桶」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明.原文链接:https://www.cnblogs.com/shuimutong/p ...

  6. Django框架——手写web框架、wsgiref模块、动静态网页、jinja2模块、主流web框架、Django简介、基本使用、app概念、目录结构、三板斧

    web应用 '''通过浏览器访问的应用程序!!!''' 1.两种模式c/s b/s B/S:browser---------------->server 2.web应用程序的有点 2.1 只需要 ...

  7. 手写DAO框架(四)-SQL执行

    -------前篇:手写DAO框架(三)-数据库连接--------- 前言 通过上一篇写的方法,可以灵活的获取.释放数据库连接,拿到连接之后,我们就可以执行sql了!所以,本篇介绍的就是SQL执行器 ...

  8. 手写MQ框架(三)-客户端实现

    一.背景 书接手写MQ框架(二)-服务端实现  ,前面介绍了服务端的实现.但是具体使用框架过程中,用户肯定是以客户端的形式跟服务端打交道的.客户端的好坏直接影响了框架使用的便利性. 虽然框架目前是通过 ...

  9. 手写DAO框架(五)-DAO层实现

    -------前篇:手写DAO框架(四)-SQL执行--------- 前言 通过上一篇,可以通过传入sql和对应的参数,可以执行sql并返回结果.但是对于一个DAO框架来说,要尽量的面向对象编程,也 ...

  10. 手写MQ框架(四)-使用netty改造梳理

    一.背景 书接上文手写MQ框架(三)-客户端实现,前面通过web的形式实现了mq的服务端和客户端,现在计划使用netty来改造一下.前段时间学习了一下netty的使用(https://www.w3cs ...

随机推荐

  1. 完美:C# Blazor中显示Markdown并添加代码高亮

    昨天发了一篇介绍这个库:C# Blazor中显示Markdown文件,介绍怎么在Blazor中显示Markdown内容的文章,文章内的代码是没有高亮的,思来相去,还是要做好,于是百度到这篇文章.NET ...

  2. AHB 设计要点

    Hreadyout 每个slave回复hreadyout通过mux给到master master会将hreadyin信号给到每个slave hreadyout开始的时候都为1,如果是为0,会出现问题, ...

  3. [kubernetes]服务健康检查

    前言 进程在运行,但是不代表应用是正常的,对此pod提供的探针可用来检测容器内的应用是否正常.k8s对pod的健康状态可以通过三类探针来检查:LivenessProbe.ReadinessProbe和 ...

  4. 【lvgl-micropython】官方源码之ports/unix 编译报错

    lv_micropython/ports/unix make 报错 编译环境如下 这是缺少SDL2库导致的 sudo apt-get install libsdl2-2.0 sudo apt-get ...

  5. WebApi使用Swagger

    services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API ...

  6. 代码使我头疼之React初学习

    前言 开始了,去年(2020)说要学的React,到现在2021年的12月底了,才来实施--(年底警告!年末总结还没开始写!) 不过前端为啥要学React呢?Vue不是很好用吗?Vue确实很好用,并且 ...

  7. [转帖]Prometheus-使用python开发exporter

    exporter有很多,但想要特定需求的话,还需自行开发.在这里使用python写一个exporter,用于监控/root下的目录数量. 开发exporter需要使用prometheus_client ...

  8. [转帖]linux audit审计(8)--开启audit对系统性能的影响

    https://www.cnblogs.com/xingmuxin/p/8875783.html 我们使用测试性能的工具,unixbench,它有一下几项测试项目: Execl Throughput ...

  9. [转帖]利用Python调用outlook自动发送邮件

    ↓↓↓欢迎关注我的公众号,在这里有数据相关技术经验的优质原创文章↓↓↓ 使用Python发送邮件有两种方式,一种是使用smtp调用邮箱的smtp服务器,另一种是直接调用程序直接发送邮件.而在outlo ...

  10. [转帖]IPv6地址解析库,窥探IPv6地址中包含的信息

    https://zhuanlan.zhihu.com/p/479028720 大家好,我是明说网络的小明同学. 今天和大家介绍一个IPv6 地址解析库IPv6 address Parser :http ...