如何避免并发重复查询

时间:2018-12-05 03:25:27

标签: java concurrency

对于给定的请求,例如/api/user/1,假设从数据库中查询将花费10s。

因此,首先,服务器接受了一个请求,该服务开始查询数据库。然后在查询期间,可能会有更多传入的相同请求(全部用于/api/user/1)。

如何避免以后的请求查询数据库?我们并不是说缓存,我们只是想避免完全相同的查询同时发生

这有意义吗?如果是,该如何做(以Java或节点应用程序为例)?

1 个答案:

答案 0 :(得分:0)

您可以使用"/api/user/1"(请参阅here),该映射将List之类的路由字符串映射到T的回调,这些回调采用单个T类型的参数(将request替换为用于存储请求结果的任何类)。您的Consumer<T>方法将需要使用Consumer<T>进行回调。如果您不熟悉T,则可以阅读有关here的信息。它只是表示一个函数的接口,该函数采用一个register类型的参数,但不返回任何内容。

当线程希望将请求的结果发送给路由时,应调用同步方法String,该方法将路由作为Map和回调并执行以下操作:

它应该检查路由是否是Map中的键。如果不是,则应将路由作为关键字添加到List,其值应为resolve,其中包含一个值,参数中提供了回调,然后应使用我将在下面讨论方法Map的回调。如果路由已经是List中的键,那么只需将线程的回调添加到Map中的resolve中,其中路由是键。

String函数应将路由视为T,并将请求结果键入List。然后,它将在路由键处获取Map,从public abstract class CallbackHandler<T> { private QueryRepetitionHandler<T> handler; private CountDownLatch latch; private T result; public CallbackHandler(QueryRepetitionHandler<T> handler) { this.handler = handler; latch = new CountDownLatch(1); } public void resolve(T result) { this.result = result; latch.countDown(); } public void request(String route) { handler.register(route); latch.await(); } } 中删除路由键,最后遍历所有回调并使用请求结果对其进行调用。

我已经用示例编写了一些代码,但是我没有对其进行测试。

CallbackHandler.java

public abstract class QueryRepetitionHandler<T> {
    private Map<String, List<CallbackHandler<T>> map = new ConcurrentHashMap<>();

    protected abstract void request(String route, Consumer<T> consumer);

    public synchronized void register(String route, CallbackHandler<T> handler) {
        if (map.containsKey(route)) {
            map.get(route).add(callback);
        } else {
            List<CallbackHandler<T>> list = new ArrayList<>();
            list.add(callback);
            map.put(route, list);
            request(route, result -> resolve(route, result));
        }
    }

    private void resolve(String route, T result) {
        List<Consumer<T>> list = map.remove(route);

        // Sanity check
        if (list != null) {
            list.forEach(handler -> handler.resolve(result));
        }
    }
}

QueryRepetitionHandler.java

QueryRepetitionHandler<T>

您将要实例化一个CallbackHandler<T>,以供所有线程共享。当线程想要发出请求时,应使用共享的QueryRepetitionHandler<T>实例化QueryRepetitionHandler<T>。当然,如果不实现request方法,就无法实例化requestConsumer<T>方法应该简单地发出请求,然后调用作为request提供的回调。然后,它应使用所需的路由作为CallbackHandler<T>参数来调用String的{​​{1}}方法。然后,该线程将等待(使用latch.await())请求的结果,直到QueryRepetitionHandler<T>调用带有结果的resolve方法并调用latch.countDown()为止。