【问题标题】:How to make other threads wait for a given task result如何让其他线程等待给定的任务结果
【发布时间】:2016-04-12 13:38:07
【问题描述】:

我对服务器进行了繁重的操作,我们称之为String getData(),我希望始终获取此数据的更新版本,因此我不对调用应用缓存。

我现在的目标是避免同时运行多个getData 呼叫。在第一次调用之后(但在第一次请求结束之前)完成所有调用以等待第一个结果。

例子:

Thread 1           Thread 2              Thread 3            
getData()
                   getData()[waiting]
                                         getData()[waiting]
result1 received   return result1        return result1
                   getData()
                   result2 received
                   return result2

我怎样才能做到这一点?

【问题讨论】:

  • 所以你只想要一个Thread 曾经运行过getData?然后这样做——有一个Thread,当getData被调用时,阻塞直到它完成——在此期间返回一个Future。当该呼叫完成时,complete Future 并等待下一个呼叫。
  • 有 3 次调用来自应用程序不同点的数据。
  • 这三个都可以等待相同的倒计时或其他东西,然后收集结果。
  • 你的 getData() 它返回什么?你能举一个你的代码例子吗?如果它只是一个字符串,您应该查看 StringBuffer(这是线程安全的)或者如果您使用列表,请替换为 CopyOnWriteArraySet
  • 这似乎是对使用 executor 服务和 Future: winterbe.com/posts/2015/04/07/… 进行线程化的一个很好的介绍

标签: java multithreading synchronization locking


【解决方案1】:

我相当不雅的想法是在第一个呼叫进入时存储Future,并在第一个呼叫仍处于未决状态时将相同的未来返回给收到的其他呼叫。然后,当第一次调用完成时,丢弃这个Future,并在下一个请求到来时创建一个新的:

class OneAtATime<T> {
  private final ExecutorService executor = Executors.newFixedThreadPool(1);

  private final Supplier<T> supplier;

  private Future<T> future;

  OneAtATime(Supplier<T> supplier) {
    this.supplier = supplier;
  }

  synchronized Future<T> submit() {
    if (future == null) {
      future = CompletableFuture.supplyAsync(supplier, executor);
      future.thenRunAsync(() -> {
        synchronized (JustOneExecutor.this) {
          future = null;
        }
      }, executor);
    }
    return future;
  }
}

【讨论】:

  • 使用 Java 8 可以稍微整理一下。
  • @kstandell - 您的编辑是正确的。社区不应该拒绝它 - 已申请。
  • @BoristheSpider 使用 Java 8 进行了一些整理。
【解决方案2】:

不涉及任何额外线程的简单解决方案是使用ConcurrentHashMap#computeIfAbsent

private final ConcurrentHashMap<String, String> instance = 
    new ConcurrentHashMap<>(1);

private String getData() {
    final AtomicBoolean computed = new AtomicBoolean(false);
    String data = instance.computeIfAbsent("KEY", () -> { 
        String data = internalGetData(); 
        computed.set(true);
        return data;
    });
    if(computed.get()) {
       instance.clear();
    }
    return data;
}

private String internalGetData() {
    // ...
}

【讨论】:

    【解决方案3】:

    您正在描述BlockingQueue 的功能。

    BlockingQueue<Data> dataQueue = new ArrayBlockingQueue(1);
    

    现在你需要做的就是dataQueue.take(),只有一个线程会得到它自己的数据。

    【讨论】:

    • 我认为这不是 OP 想要的。我知道他想合并请求。含义:第一个请求从远程服务器开始数据收集。数据收集完成后,所有后续请求都应排队并收到相同的结果。现在所有“请求者”都有相同的数据。下一个请求将向远程服务器发起一个新请求。
    • @Fildor - 我解释说 我希望始终获取此数据的更新版本,因此我不会对调用应用缓存,因为 从不提供可能过时的数据数据,即调用必须返回一个新的datum
    • @OldCurmudgeon 我认为这个想法是调用该方法,然后每个下一个调用都会阻塞结果。一旦方法返回,所有等待的线程都会得到结果。然后系统重置。
    • 我提到了问题的第二段。
    • @Fildor - 令人困惑。我认为可以使用BlockingQueue&lt;Future&lt;Data&gt;&gt; 将其更改为您的解释。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-18
    • 2020-09-21
    • 1970-01-01
    • 1970-01-01
    • 2016-05-01
    • 2010-12-26
    相关资源
    最近更新 更多