当前位置: 首页 > 面试题库 >

Java执行程序:如何在任务完成时无阻碍地得到通知?

柴默
2023-03-14
问题内容

假设我有一个充满任务的队列,需要将其提交给执行者服务。我希望他们一次处理一个。我能想到的最简单的方法是:

  1. 从队列中接任务
  2. 提交给执行者
  3. 在返回的Future上调用.get并阻塞,直到获得结果为止
  4. 从队列中执行另一个任务…
    但是,我试图避免完全阻止。如果我有10,000个这样的队列,需要一次处理一个任务,那么我的堆栈空间将用完,因为它们中的大多数都将保留阻塞的线程。

我想要提交一个任务并提供一个回叫,当任务完成时会调用该回叫。我将使用该回调通知作为发送下一个任务的标志。(functionaljava和jetlang显然使用了这种非阻塞算法,但我无法理解它们的代码)

如果不编写自己的执行程序服务,如何使用JDK的java.util.concurrent做到这一点?

(向我提供这些任务的队列本身可能会阻塞,但这是稍后要解决的问题)


问题答案:

定义一个回调接口,以接收要在完成通知中传递的任何参数。然后在任务结束时调用它。

你甚至可以为Runnable任务编写通用包装,并将其提交给ExecutorService。或者,请参阅下面的Java 8中内置的机制。

class CallbackTask implements Runnable {

  private final Runnable task;

  private final Callback callback;

  CallbackTask(Runnable task, Callback callback) {
    this.task = task;
    this.callback = callback;
  }

  public void run() {
    task.run();
    callback.complete();
  }

}

使用CompletableFutureJava 8,Java 8包含了一种更精细的方法来组成管道,在该管道中可以异步和有条件地完成进程。这是一个人为但完整的通知示例。

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

public class GetTaskNotificationWithoutBlocking {

  public static void main(String... argv) throws Exception {
    ExampleService svc = new ExampleService();
    GetTaskNotificationWithoutBlocking listener = new GetTaskNotificationWithoutBlocking();
    CompletableFuture<String> f = CompletableFuture.supplyAsync(svc::work);
    f.thenAccept(listener::notify);
    System.out.println("Exiting main()");
  }

  void notify(String msg) {
    System.out.println("Received message: " + msg);
  }

}

class ExampleService {

  String work() {
    sleep(7000, TimeUnit.MILLISECONDS); /* Pretend to be busy... */
    char[] str = new char[5];
    ThreadLocalRandom current = ThreadLocalRandom.current();
    for (int idx = 0; idx < str.length; ++idx)
      str[idx] = (char) ('A' + current.nextInt(26));
    String msg = new String(str);
    System.out.println("Generated message: " + msg);
    return msg;
  }

  public static void sleep(long average, TimeUnit unit) {
    String name = Thread.currentThread().getName();
    long timeout = Math.min(exponential(average), Math.multiplyExact(10, average));
    System.out.printf("%s sleeping %d %s...%n", name, timeout, unit);
    try {
      unit.sleep(timeout);
      System.out.println(name + " awoke.");
    } catch (InterruptedException abort) {
      Thread.currentThread().interrupt();
      System.out.println(name + " interrupted.");
    }
  }

  public static long exponential(long avg) {
    return (long) (avg * -Math.log(1 - ThreadLocalRandom.current().nextDouble()));
  }

}


 类似资料:
  • 当用户无法登录他们的帐户时,我正在尝试执行警报。也就是说,当我尝试使用下面的代码完成此操作时,我会抛出以下错误: 原因:java.lang.RuntimeException:无法在未调用Looper.prepare()的线程[AsyncTask#1,5,main]内创建处理程序 我怎样才能解决这个问题?请参阅下面的代码。 登录活动

  • 问题内容: 我的问题与这里的这个问题密切相关。如此处所述,我希望主线程等待,直到工作队列为空并且所有任务都已完成。但是,我的情况是每个任务都可能递归地导致新任务被提交进行处理。这使得收集所有这些任务的未来变得有点尴尬。 我们当前的解决方案使用忙等待循环来等待终止: numTasks是随着创建每个新任务而增加的值。这可以工作,但是由于繁忙的等待,我认为它不是很好。我想知道是否有一个好方法可以使主线程

  • 我已经创建了3个任务。Task3取决于Task1和Task2的结果。在调试代码时,它会正确执行,但在运行应用程序时,Task3会在Task1和Task2完成之前执行。 示例代码: 提前谢谢。

  • 问题内容: 我正在构建一个基于Netty的小型应用程序,该应用程序通过套接字连接(即telnet / ssh)执行I / O操作。我正在用Netty的类启动我的套接字服务器,给它: 类型的事件循环(即不应接受阻塞操作的共享线程池)。 一个类型的通道(我相信这必须与上面的#1相对应)。 一个非常简单的管道,带有可扩展的通道处理程序。 每当从客户端套接字连接接收到命令字符串时,都会调用我的处理程序的方

  • 我正在运行的服务器应用程序获取我要使用任务系统处理的多个任务请求。 每个任务都表示为一个可运行的线程池,该线程池中的线程数小于或等于线程池的大小,需要线程池中的线程数。当然,线程池是必要的,以避免CPU因线程过多而过载。 然而,其中一些任务可以是多线程的,而另一些则不能。这就是为什么一个任务可能需要等待其所有特定线程完成,以便合并这些线程的结果以获得最终结果的原因。 如果使用多个实例,则可以像这样

  • 问题内容: 詹金斯中有什么方法可以阻止给定任务执行所有下游任务?我有一系列功能测试阶段,每个阶段都需要在部署后针对服务器运行。服务器应该 不会 被运行测试时更新。但是,。 所以我有工作: 我需要阻止直到完成。 我怎样才能做到这一点? 问题答案: 看一下项目A的高级项目选项“在构建下游项目时阻止构建”选项。我相信这将阻止A的多个实例运行,直到所有下游项目(即B和C)都已完成。